# Send email with .NET

> Send email from ASP.NET Core and .NET with the Emailit NuGet package, MailKit or System.Net.Mail over SMTP, and verify Emailit webhooks.

This guide shows how to send email from a .NET app with the official `Emailit` NuGet package, how to use MailKit or `System.Net.Mail` over SMTP instead, and how to verify webhooks in an ASP.NET Core minimal API.

## Prerequisites

- .NET 8 or later.
- A [verified sending domain](/docs/domains/add-a-domain/), for example `acme.com`.
- An [API key](/docs/developers/api-keys/). A Sending Only key restricted to your domain is enough.
- Until your workspace has [production access](/docs/workspaces/production-access/), you can only send to the account emails of workspace members.

## Install the SDK

```bash
dotnet add package Emailit
```

## Configure your API key

ASP.NET Core reads configuration from environment variables, where `__` separates sections. Set `Emailit__ApiKey` in production:

```bash
```

During development, keep the key in user secrets instead of `appsettings.json`:

```bash
dotnet user-secrets init
dotnet user-secrets set "Emailit:ApiKey" "secret_••••••••••••••••••••••••••••••••"
```

## Send an email

Register one client and inject it into your endpoints:

```csharp title="Program.cs"
using Emailit;
using Emailit.Options;
using Emailit.Resources;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton(new EmailitClient(builder.Configuration["Emailit:ApiKey"]!));

var app = builder.Build();

app.MapPost("/orders/{id}/confirm", (string id, EmailitClient emailit) =>
{
    Email email = emailit.Emails.Send(new EmailSendOptions
    {
        From = "Acme <orders@acme.com>",
        To = new[] { "ada@example.com" },
        Subject = $"Order {id} confirmed",
        Html = $"<p>We've received order {id}.</p>",
    });

    return Results.Ok(new { emailId = email.Id });
});

app.Run();
```

`Send` is synchronous; for bulk sends, run it from a background service so request threads stay free. The options also accept `Cc`, `Bcc`, `ReplyTo`, `Attachments`, `Template` with `Variables`, `ScheduledAt` and `Tracking`; see [Send an email](/docs/api-reference/emails/send/).

If your code already builds `System.Net.Mail.MailMessage` objects, the SDK can send them through the API:

```csharp
using System.Net.Mail;

var message = new MailMessage("orders@acme.com", "ada@example.com", "Order confirmed", "<p>Thanks!</p>")
{
    IsBodyHtml = true,
};

Email email = emailit.Emails.Send(message);
```

### Handle errors

```csharp
using Emailit.Exceptions;

try
{
    emailit.Emails.Send(options);
}
catch (RateLimitException)
{
    // 429: back off and retry
}
catch (AuthenticationException)
{
    // 401: the API key is missing or invalid
}
catch (UnprocessableEntityException ex)
{
    // 422: for example, the from domain isn't verified
    logger.LogWarning("Emailit rejected the email: {Message}", ex.Message);
}
catch (ApiErrorException ex)
{
    logger.LogError("Emailit error {Status}: {Body}", ex.HttpStatus, ex.HttpBody);
}
```

## Send with SMTP instead

### MailKit

Microsoft recommends [MailKit](https://github.com/jstedfast/MailKit) for new SMTP code. Install it with `dotnet add package MailKit`:

```csharp
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

var message = new MimeMessage();
message.From.Add(new MailboxAddress("Acme", "orders@acme.com"));
message.To.Add(MailboxAddress.Parse("ada@example.com"));
message.Subject = "Order confirmed";
message.Body = new TextPart("html") { Text = "<p>We've received your order.</p>" };

using var smtp = new SmtpClient();
await smtp.ConnectAsync("smtp.emailit.com", 587, SecureSocketOptions.StartTls);
await smtp.AuthenticateAsync("emailit", builder.Configuration["Emailit:ApiKey"]);
await smtp.SendAsync(message);
await smtp.DisconnectAsync(true);
```

For implicit TLS, connect to port 465 with `SecureSocketOptions.SslOnConnect`.

### System.Net.Mail

The built-in client works for simple cases. It only supports STARTTLS, so use port 587 (or 2525 or 2587):

```csharp
using System.Net;
using System.Net.Mail;

using var client = new SmtpClient("smtp.emailit.com", 587)
{
    EnableSsl = true,
    Credentials = new NetworkCredential("emailit", apiKey),
};

client.Send(new MailMessage("orders@acme.com", "ada@example.com", "Order confirmed", "We've received your order."));
```

See [SMTP settings](/docs/smtp/settings/) for every port.

## Receive webhooks

[Create a webhook](/docs/webhooks/set-up/) and store its signing secret as `Emailit:WebhookSecret`. Read the raw body, verify it, then parse the array of events:

```csharp title="Program.cs"
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

app.MapPost("/webhooks/emailit", async (HttpRequest request, IConfiguration config) =>
{
    using var reader = new StreamReader(request.Body, Encoding.UTF8);
    var body = await reader.ReadToEndAsync();

    var signature = request.Headers["X-Emailit-Signature"].ToString();
    var timestamp = request.Headers["X-Emailit-Timestamp"].ToString();

    if (!long.TryParse(timestamp, out var ts) ||
        Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - ts) > 300)
    {
        return Results.Unauthorized();
    }

    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(config["Emailit:WebhookSecret"]!));
    var expected = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes($"{timestamp}.{body}"))).ToLowerInvariant();

    if (!CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(signature)))
    {
        return Results.Unauthorized();
    }

    // The body is a JSON array of up to 100 events.
    using var events = JsonDocument.Parse(body);
    foreach (var evt in events.RootElement.EnumerateArray())
    {
        if (evt.GetProperty("type").GetString() == "email.bounced")
        {
            var address = evt.GetProperty("data").GetProperty("object").GetProperty("to").GetString();
            // Stop emailing this address.
        }
    }

    return Results.Ok();
});
```

Return a `2xx` within 30 seconds; other responses are retried. See [Request signature](/docs/webhooks/request-signature/).

## Production tips

- **Register the client as a singleton.** One `EmailitClient` per app is enough.
- **Queue bulk mail.** Send from a `BackgroundService` or a job library such as Hangfire, and throttle it to stay under your [sending limits](/docs/limits/) (2 emails per second by default).
- **Make retries safe.** If you retry after a timeout, send an `Idempotency-Key` header with `HttpClient`; see [Idempotency](/docs/email-api/idempotency/).
- **Deduplicate webhooks** by storing each `event_id` you process.

## Next steps

  - [Send email with the API](/docs/email-api/send-email/): Attachments, scheduling and tracking.
  - [Webhook event types](/docs/webhooks/event-types/): Every event and its payload.
  - [SMTP troubleshooting](/docs/smtp/troubleshooting/): Fix authentication and connection errors.
  - [SDKs and libraries](/docs/sdks/): All official libraries.

---
Source: https://emailit.com/docs/frameworks/dotnet/
