How to Send Mail from C#: The Complete 2026 Guide
Learn how to send mail from C# using SmtpClient, MailKit, and the agent-native Robotomail API. This guide has code examples, tips, and troubleshooting for 2026.

Your C# app needs to send an email, and the answer depends on what kind of system you’re building.
If you’re maintaining an older .NET application, you’ll still run into System.Net.Mail.SmtpClient. If you’re building a normal modern service, background worker, or SaaS app, MailKit is the practical default. If you’re building an autonomous workflow that has to send, receive, and keep track of replies without a human mailbox in the loop, SMTP starts to feel like the wrong abstraction.
That’s the true shape of the problem in 2026. The syntax matters, but the architecture matters more. Teams that follow solid key software development best practices usually make this decision based on maintainability, security, and operational fit, not just on which code sample compiles first.
Your Options for Sending Email in C#
There are three real paths when you need to send mail from c#.
The first is the old built-in route. SmtpClient shipped with .NET Framework years ago, and a lot of production code still contains it. You should understand it because legacy systems still rely on it. You shouldn’t choose it for new work unless you’re boxed in by an existing codebase.
The second path is the modern library route. MailKit replaced the old default in practice because it handles today’s email requirements better, especially MIME-heavy messages, async sending, and secure SMTP sessions. For most new .NET applications, this is the answer that causes the fewest regrets later.
The third path is API-first, mailbox-aware infrastructure for autonomous systems. That matters when email isn’t just a notification channel but part of a workflow loop. Sending is easy. Receiving, parsing, threading, and continuing the conversation is where traditional tutorials usually stop being useful.
What actually changes your choice
The decision usually comes down to these questions:
- Are you maintaining legacy code? If yes, you may need to keep
SmtpClientrunning while planning a migration. - Are you sending transactional or operational mail from a normal app? Use MailKit unless you have a very specific reason not to.
- Does your system need to handle replies automatically? Then outbound SMTP alone won’t cover the full job.
Practical rule: pick the tool based on the full mail lifecycle your app owns, not just the first outbound send.
A password reset email, a daily report, and an AI agent negotiating over email are all “send email” tasks. They are not the same engineering problem.
The Legacy Method Using SmtpClient
System.Net.Mail.SmtpClient is the class many C# developers learned first. It was introduced with the .NET Framework era and made email sending feel straightforward because it was built in, required no extra package, and worked with a small amount of code.

If you inherit an older app, you’ll likely see something close to this:
A minimal legacy example
using System.Net;
using System.Net.Mail;
var message = new MailMessage();
message.From = new MailAddress("sender@example.com");
message.To.Add(new MailAddress("recipient@example.com"));
message.Subject = "Test email";
message.Body = "Sent from a legacy C# app.";
message.IsBodyHtml = false;
using var smtp = new SmtpClient("smtp.gmail.com", 587);
smtp.Credentials = new NetworkCredential("username", "password");
smtp.EnableSsl = true;
await smtp.SendMailAsync(message);
That pattern still works in plenty of old systems. You create a MailMessage, set host, port, credentials, and SSL, then send.
Why this is legacy, not modern
The problem isn’t that SmtpClient can’t send mail. It’s that it no longer matches how modern .NET applications should handle email infrastructure, credentials, async behavior, and maintenance risk.
Microsoft marked SmtpClient as obsolete in .NET 5 in November 2020, and its removal is anticipated post-.NET 9. The same source notes that an estimated 40% of legacy C# codebases were still relying on it as of 2023, which is exactly why so many teams still encounter it in production code (mailtrap analysis of SmtpClient deprecation).
If you see
System.Net.Mail.SmtpClientin a new code review, treat it as technical debt on day one.
When it still makes sense
There are only a few defensible reasons to keep using it:
- You’re maintaining an established app and changing the mail stack would create unnecessary release risk.
- You need a short-term compatibility layer while moving toward a newer library.
- Your outbound volume and complexity are low, and the surrounding environment is already fixed.
Common problems in real projects
The ugly parts tend to show up outside the happy-path sample:
- Credential handling: SMTP usernames and passwords often end up in config files, environment variables, or secrets stores that people don’t manage carefully enough.
- TLS confusion: Port and SSL mismatches are a common cause of connection failures.
- Provider-specific auth: Gmail often needs app passwords rather than a normal account password.
- Limited future value: Every hour spent expanding a new
SmtpClientintegration is effort you’ll likely replace later.
If your task is “keep the old thing working,” learn it. If your task is “build the right thing now,” move on.
The Modern Standard Using MailKit
For most new code, MailKit is the right default when you need to send mail from c# over SMTP.
MailKit became the practical standard after SmtpClient fell out of favor. According to elmah.io’s MailKit tutorial, it has over 12 million NuGet downloads, its adoption surged 300% post-.NET Core 3.1, and its MimeKit parser correctly handles 99.9% of RFC-compliant messages.

That lines up with what teams see in practice. MailKit feels like a library written for modern .NET, not one carried forward from a much older framework era.
The baseline setup
Install the package first:
dotnet add package MailKit
Then build and send a message with MimeMessage and MailKit’s SMTP client:
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
var msg = new MimeMessage();
msg.From.Add(new MailboxAddress("App", "sender@domain.com"));
msg.To.Add(new MailboxAddress("Recipient", "to@example.com"));
msg.Subject = "Hello from C#";
msg.Body = new TextPart("plain")
{
Text = "This message was sent with MailKit."
};
using var client = new SmtpClient();
await client.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
await client.AuthenticateAsync("username", "password");
await client.SendAsync(msg);
await client.DisconnectAsync(true);
The important part is the shape of the flow. Connect with STARTTLS on port 587, authenticate explicitly, send asynchronously, then disconnect cleanly.
Why developers prefer it
MailKit fixes several pain points that made the old built-in class frustrating:
- Async-first behavior:
awaitpatterns fit background jobs, APIs, and worker services much better. - Better MIME support: Building plain text, HTML, multipart bodies, and attachments is cleaner.
- Cross-platform use: It fits current .NET deployment patterns.
- Clear separation of concerns:
MimeKitbuilds the message, MailKit sends it.
Working habit: use MailKit’s
SmtpClient, notSystem.Net.Mail.SmtpClient. The class name overlap trips people up more often than it should.
HTML and attachments
A lot of email code becomes brittle because developers hand-roll MIME details they shouldn’t be hand-rolling. Use BodyBuilder instead.
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Reports", "reports@domain.com"));
message.To.Add(new MailboxAddress("User", "user@example.com"));
message.Subject = "Your weekly report";
var builder = new BodyBuilder();
builder.TextBody = "Your report is attached.";
builder.HtmlBody = "<p>Your <strong>weekly report</strong> is attached.</p>";
builder.Attachments.Add("report.pdf");
message.Body = builder.ToMessageBody();
using var smtp = new SmtpClient();
await smtp.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
await smtp.AuthenticateAsync("username", "password");
await smtp.SendAsync(message);
await smtp.DisconnectAsync(true);
MailKit starts to earn its keep because you describe the message you want, and the library handles the MIME structure properly.
A walkthrough helps if you want to see the pattern in motion:
What still trips people up
MailKit is better, but it doesn’t eliminate SMTP realities.
- STARTTLS matters: The Dometrain tutorial notes that
SecureSocketOptions.StartTlsis the right choice for this pattern, and misuse is a major source of connection failures. - Gmail auth is special: Gmail with 2FA typically needs an app password, not the normal account password.
- You still own deliverability: A successful SMTP send doesn’t guarantee inbox placement.
- Threading is still manual: If your app needs coherent conversations, you need to manage message headers and reply context deliberately.
For standard application email, this is still the sweet spot. You get control without dropping into the maintenance trap of old framework code.
The Agent-Native Approach with Robotomail
Traditional C# email tutorials spend almost all their time on outbound sending. That’s useful if you only need receipts, alerts, or one-way notifications. It falls apart when the application itself has to participate in a conversation.
That gap is larger than most developers expect. Analysis of top C# email tutorials found that less than 5% address how to programmatically receive and parse replies, which is exactly why teams end up wrestling with IMAP clients, OAuth edge cases, or browser automation hacks instead of building the workflow they wanted (Courier’s analysis of the inbound email gap).

Where SMTP stops being enough
SMTP libraries solve a narrow problem: send this message to that server.
Autonomous systems need more than that:
- a mailbox the system can own programmatically
- a way to receive inbound messages without bolting on a separate mail stack
- reliable threading so the agent knows which reply belongs to which conversation
- machine-friendly delivery paths like webhooks, polling, or event streams
That’s why API-first mail infrastructure is a different category from a better SMTP client. It changes the operating model.
A simple C# API pattern
If the provider exposes a straightforward HTTP API, sending mail becomes ordinary application code instead of SMTP session management. A basic pattern looks like this:
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "YOUR_API_TOKEN");
var payload = new
{
from = "agent@your-domain.com",
to = new[] { "user@example.com" },
subject = "Following up",
text = "I received your request and need one more detail."
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await http.PostAsync("https://api.example.com/send", content);
response.EnsureSuccessStatusCode();
The point isn’t that HTTP is magically simpler than SMTP in all cases. The point is that for application developers, HTTP integrates more naturally with the rest of the stack: auth tokens, retries, observability, JSON payloads, webhooks, and event-driven processing.
Why this matters for autonomous workflows
For AI agents, outbound send is only the first turn. The hard part is what happens after the human replies.
A purpose-built platform such as Robotomail’s API quick start takes the mailbox itself into account. According to the product information provided for this article, it supports mailbox creation through an API call, outbound sending through a POST request, inbound handling through webhooks, server-sent events, or polling, HMAC-signed event delivery, and automatic threading for conversation context preservation.
Email for agents isn’t just “send a message.” It’s “own the conversation loop without a human inbox workaround.”
That distinction matters. If your application is a normal web app, MailKit is usually enough. If your application is an autonomous actor, SMTP starts looking like a transport detail wrapped around missing infrastructure.
Choosing Your C# Email Method a Comparison
Teams don’t need philosophy here. They need a clear decision.
The simplest way to choose is to match the tool to the operational job. Legacy maintenance is one job. Modern application mail is another. Autonomous conversation handling is a third.
C# Email Method Comparison
| Criterion | SmtpClient (Legacy) | MailKit (Modern) | Robotomail (Agent-Native) |
|---|---|---|---|
| Best fit | Maintaining older .NET apps already built around System.Net.Mail |
New .NET apps that need reliable outbound email over SMTP | Systems that need programmatic mailboxes plus send-and-receive workflows |
| Setup model | Built in, but tied to older patterns and older assumptions | NuGet package plus explicit SMTP configuration | API-driven workflow with HTTP requests and inbound event handling |
| Developer experience | Familiar in old code, awkward in new code | Clean async patterns and stronger MIME handling | Feels like integrating an application service rather than an SMTP session |
| Security posture | Often pushes teams toward direct SMTP credential handling | Better modern library design, but you still manage SMTP provider details | Better suited when mailbox lifecycle and inbound handling must be automated |
| Attachments and HTML | Possible, but dated ergonomics | Strong support through MimeKit and BodyBuilder |
Depends on API design rather than MIME composition in your code |
| Inbound replies | Not covered by the sending class itself | Possible only by adding separate IMAP or POP handling | Built for workflows where receiving replies is part of the core use case |
| Threading conversations | Manual and easy to get wrong | Manual, but easier to structure cleanly | Better fit when conversation context is a first-class requirement |
| Recommendation | Keep only for legacy support and migration windows | Default choice for most new outbound email work | Choose when the app behaves like an autonomous email participant |
The short version
- Use SmtpClient only when old code already depends on it.
- Use MailKit for almost every new outbound SMTP implementation.
- Use an agent-native API model when your application needs full conversation handling instead of just dispatching messages.
If your team says “we just need to send some emails,” MailKit is probably enough. If the next sentence is “and then process the replies automatically,” your architecture decision changes immediately.
Troubleshooting Common C# Email Issues
Email bugs often look random from the application side. They usually aren’t. Most failures come from a small set of repeat offenders: authentication, transport security, deliverability, and broken reply context.
Authentication failures
If your code compiles and the server still rejects the login, check the account model before anything else.
- Wrong credential type: Some providers expect an API key used as a password or a special username value.
- Gmail account rules: Gmail commonly requires an app password rather than the normal mailbox password.
- Expired or rotated secrets: This is routine in production and easy to overlook during incident response.
When the error mentions secure connections or authentication, don’t start by rewriting the message object. Start with credentials and provider requirements.
TLS and port mismatches
This is another frequent source of pain. The SMTP host, port, and TLS mode must agree with each other.
A practical baseline for modern SMTP sending is:
- Use port 587 with STARTTLS when the provider supports that mode.
- Avoid guessing SSL settings based on old snippets copied from forums.
- Log connection configuration carefully without logging secrets.
Debugging note: if the failure happens before auth, suspect host, port, or TLS mode. If it happens after auth, suspect credentials or account policy.
Deliverability problems
A successful send call only means the message was accepted for delivery. It doesn’t mean the message will land in the inbox.
Common reasons mail ends up in spam or gets filtered aggressively include:
- Thin sender reputation
- Missing alignment between sending setup and your domain
- HTML-only content with no plain text alternative
- Messages that look machine-generated in the worst possible way
If your mail is being accepted but not seen, inspect the full path. Message construction, provider configuration, and sender reputation all matter. If you’re working through a persistent failure pattern, this practical guide on why mail is not sending is a useful checklist.
Broken threading and lost context
This one shows up in support systems, automated follow-ups, and agent workflows. The app sends a reply, but the next message no longer appears in the same conversation.
Use message headers deliberately:
- Set
In-Reply-Towhen replying to a known message. - Preserve
Referencesif you want clients to keep conversation history linked. - Store message identifiers in your own database instead of assuming the mailbox provider will solve state for you.
A practical checklist
- Verify auth first. Provider-specific login rules break more integrations than message formatting does.
- Confirm TLS mode and port together. Don’t change one without the other.
- Send plain text and HTML. It improves compatibility and reduces weird client behavior.
- Track message IDs. Threading depends on them.
- Separate send success from inbox success. Those are different operational outcomes.
Most email bugs become manageable once you stop treating “send mail from c#” as one problem. It’s several smaller ones stacked together.
The Right Tool for Your Email Task
The clean answer is narrower than most comparison posts make it sound.
If you’re maintaining a legacy .NET application, keep SmtpClient running only as long as you need to. Don’t invest in it as if it were part of your future architecture. Stabilize it, isolate it, and put migration on the roadmap.
If you’re building a new application that needs dependable outbound email, MailKit should be your default. It fits modern .NET development, handles message composition properly, and avoids the dead-end feeling of extending older framework code. For projects shipping transactional or operational email, that’s the right balance of control and practicality.
If your system needs to act like an autonomous participant in email, the problem changes. You’re no longer choosing a syntax for sending. You’re choosing infrastructure for mailbox creation, inbound handling, event delivery, and context preservation. That’s where an API-first model makes more sense than treating SMTP as the whole solution.
Security should stay part of the decision all the way through. Credential storage, provider auth models, secret rotation, webhook verification, and operational visibility all matter more than most first-pass tutorials admit. A solid review of software development security best practices is worth bringing into this decision before the integration hardens into production.
The future of programmatic email isn’t just better SMTP code. It’s systems that can own the mailbox, process replies, and continue the workflow without human glue.
Pick the tool that matches the task. That’s what keeps the mail layer boring, which is exactly what you want in production.
If you’re building agents that need their own mailboxes, outbound sending, inbound webhooks, and conversation threading in one place, take a look at Robotomail.