Sending Email in .NET Core with Office 365 and MailKit

You may have previously used SmtpClient to send email in .NET. However, that API is now obsolete and the current recommend method is to use the MailKit library. Here’s how to use it with the Office 365 SMTP servers.


This is a starter guide. I have written a more advanced emailing post here.

It’s best practice to not send emails synchronously in response to user actions, particularly in a blocking manner. They are a great unit of work for running in the background (e.g. with Message Queuing) as they are not instant anyway. You can read about best practices for high performance and reliability in my book on building web apps.

This blog is also treeware. You don’t pay for it but I’d love you to buy a tree if you find it useful.

Buy me a tree!


There are various ways to send email using Office 365. Connecting directly to the MX record for an organisation looks attractive but if you don’t have a static IP address to add to the SPF (in a DNS TXT record) then you will get blocked as spam and will see an error message like this.

SmtpCommandException: 5.7.1 Service unavailable, Client host [$ConnectingIP] blocked using Spamhaus. [XXXXXXXXXX.xxxxxx.prod.outlook.com] [XXXXXXXX.xxxxxxx.prod.outlook.com] [XXXXXXX.xxxxxxx.prod.protection.outlook.com]

The best way is to authenticate directly with an Office 365 mailbox. However, there can be problems when using TLS.

Microsoft recommend the standard port of 587 and TLS enabled but this is misleading. Full TLS would use port 465.

You need to use SecureSocketOptions.StartTls. If you try to enable TLS for the whole connection (true or SecureSocketOptions.SslOnConnect) then it will fail with an error message like this.

The remote certificate is invalid according to the validation procedure

Another error message you may encounter is this.

SmtpCommandException: 5.2.0 STOREDRV.Submission.Exception:SendAsDeniedException.MapiExceptionSendAsDenied; Failed to process message due to a permanent exception with message Cannot submit message.

What this cryptic response actually means is that the from address and name specified must match the mailbox you are authenticating as.

Putting it all together, you can use the following async C# code.

var message = new MimeMessage();
message.From.Add(new MailboxAddress("{from name}", "{from email address}"));
message.To.Add(new MailboxAddress("{to name}", "{to email address}"));
message.Subject = "{subject}";

message.Body = new TextPart("plain")
{
    Text = "{body}"
};

using (var client = new SmtpClient())
{
    await client.ConnectAsync("smtp.office365.com", 587, SecureSocketOptions.StartTls);
    await client.AuthenticateAsync("{from email address}", "{from password}");
    await client.SendAsync(message);
    await client.DisconnectAsync(true);
}

Clearly many of these values should come from configuration files and not be hard coded. The password (for a test account) should be kept in your secret store (outside of source control) for development and injected using a secure variable (for the production account) during a live deployment. I’ll write more about that in my next post on Azure DevOps release pipelines.


This was a starter guide. I have written a more advanced emailing post here.

It’s best practice to not send emails synchronously in response to user actions, particularly in a blocking manner, as they are not instant anyway. If you’d like to learn more about running jobs in the background (e.g. with Message Queuing) and other best practices for high performance and reliability then you should check out my book on building web apps.


This blog is treeware! If you found it useful then please plant a tree.
Donate a treeDonate a tree🌳🌳