Get your API key
All integrations Frameworks

ASP.NET Core + MailKite

ASP.NET Core Identity defines IEmailSender (and, since .NET 8, the generic IEmailSender<TUser>) specifically so account-confirmation and password-reset mail can be swapped in with one interface implementation. MailKite.AspNetCore is that implementation — install it, set an API key and a from address, and every Identity email goes out through MailKite.

What you need

  • A verified domain with SPF + DKIM published
  • Your API key (mk_live_…)
  • ASP.NET Core with Identity — dotnet new webapp -au Individual (Razor Pages UI) or AddIdentityApiEndpoints (.NET 8+ minimal API)

Install

terminal
dotnet add package MailKite.AspNetCore

Targets net8.0+ and depends on the official MailKite .NET SDK.

Configure

Register the sender in Program.cs — this wires up both Identity email interfaces at once:

Program.cs
// Program.cs
using MailKite.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();

// Registers both IEmailSender (scaffolded Identity Razor Pages UI) and
// IEmailSender<TUser> (.NET 8+ MapIdentityApi<TUser>()) against one MailKite client.
builder.Services.AddMailKiteEmailSender(builder.Configuration, options =>
{
options.FromAddress = "hello@yourdomain.com";
options.FromName = "Your App";
});

builder.Services.AddRazorPages();

var app = builder.Build();
app.MapRazorPages();
app.Run();
appsettings.json
// appsettings.json
{
"MailKite": {
"FromAddress": "hello@yourdomain.com",
"FromName": "Your App"
}
}
.env
# Environment (never commit this)
MAILKITE_API_KEY=mk_live_...

That's it — every account-confirmation and password-reset email Identity sends now goes out through POST /v1/send.

.NET 8+ minimal API Identity

Apps using MapIdentityApi<TUser>() instead of the scaffolded Razor Pages UI get the same wiring — AddMailKiteEmailSender registers IEmailSender<TUser> too:

Program.cs
// Program.cs — .NET 8+ minimal API Identity
builder.Services.AddIdentityApiEndpoints<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddMailKiteEmailSender(builder.Configuration, options =>
{
options.FromAddress = "hello@yourdomain.com";
});

var app = builder.Build();
app.MapIdentityApi<ApplicationUser>(); // resolves IEmailSender<ApplicationUser> → MailKite

Send email directly (outside Identity)

For anything beyond Identity's account flows, inject MailKite.MailKiteClient (registered as a singleton by AddMailKiteEmailSender) and call Send() directly:

InvoiceService.cs
// Anywhere in the app — inject MailKite.MailKiteClient directly for non-Identity email
using MailKite;

public class InvoiceService(MailKiteClient mailkite)
{
public Task SendReceiptAsync(string to, string invoiceId) =>
mailkite.Send(new
{
from = "billing@yourdomain.com",
to,
subject = $"Receipt #{invoiceId}",
html = $"<p>Thanks! Receipt #{invoiceId} is attached.</p>",
});
}

Receive inbound email

MailKite can POST inbound email to a minimal API route. MailKiteClient.VerifyWebhook is a local HMAC-SHA256 check — no network call:

Program.cs
// Minimal API inbound webhook handler
app.MapPost("/webhooks/mailkite", async (HttpRequest request, IConfiguration config) =>
{
request.EnableBuffering();
using var reader = new StreamReader(request.Body, leaveOpen: true);
var rawBody = await reader.ReadToEndAsync();
request.Body.Position = 0;

var signature = request.Headers["x-mailkite-signature"].ToString();
var secret = config["MailKite:WebhookSecret"]!;

if (!MailKiteClient.VerifyWebhook(signature, rawBody, secret))
return Results.Unauthorized();

// Process the inbound email (rawBody is the JSON payload) — create a ticket, log, etc.
return Results.Text(MailKiteClient.ReplyOk(), "application/json");
});

What maps where

ASP.NET Core IdentityMailKite send field
MailKiteEmailSenderOptions.FromAddress/FromNamefrom
SendEmailAsync's email / IEmailSender<TUser>'s emailto
SendEmailAsync's subjectsubject
SendEmailAsync's htmlMessagehtml

Non-2xx API responses (unverified domain, suppressed recipient, rate limit) throw MailKiteException with the API's own error message and HTTP status — Identity's email hooks see the real failure instead of a silent no-op.

Troubleshooting

  • App fails to start with a MailKite options validation errorApiKey and FromAddress are validated at startup (.ValidateOnStart()). Set MAILKITE_API_KEY and MailKite:FromAddress.
  • Confirmation/reset emails never arrive — check that FromAddress is on a domain verified (SPF + DKIM) with MailKite; unverified-domain sends are rejected by the API and surface as a thrown MailKiteException in your logs.
  • Registered a custom IEmailSender and MailKite isn't usedAddMailKiteEmailSender uses Replace, so whichever is registered *last* wins if you also register your own; call order between it and your own registration matters (Identity's own default doesn't, since MailKite always replaces that).

Full package reference: MailKite.AspNetCore on GitHub (once published) · MailKite .NET SDK for direct API access · Inbound webhooks for the full payload.