Send with your stack

Send emails with .NET & C#

Send email from C# with the official Mailblastr NuGet package — async, typed, BCL-only, and ready for IHttpClientFactory.

The official `Mailblastr` NuGet package (5.0.0) is the fastest way to send email from C#, F# or VB. It targets net8.0 and takes no dependency beyond the BCL — HttpClient plus System.Text.Json. Every call is async, returns a typed model, and accepts an optional CancellationToken.

You create one client and reach every operation through a flat method on the IMailblastr interface — EmailSendAsync, ContactCreateAsync, CampaignCreateAsync, and so on. Create it once and reuse it for the lifetime of the process: it holds a pooled HttpClient and is safe to share across threads.

Prerequisites

  1. A verified domain for your from address — publish its SPF/DKIM/DMARC records first. (guide)
  2. An API key (mb_...), read from MAILBLASTR_API_KEY. (Authentication)
  3. .NET 8.0 or newer. The package targets net8.0 only, so a net6.0/net7.0 or .NET Framework project cannot restore it.

Install

dotnet add package Mailblastr --version 5.0.0

Drop --version to take the newest release. The package id is Mailblastr — one capital M, the rest lower case — and the root namespace matches it, so a single using Mailblastr; brings in the client, the request models and the exception type.

Send an email

MailblastrClient.Create(apiKey) hands back the IMailblastr interface, and EmailSendAsync posts to /emails. Address your first message to delivered@mailblastr.dev, the delivery simulator: it is intercepted before it reaches a real mailbox, so it is safe to send to while you are wiring things up. Do not use example.com — it is a blocked recipient domain and the send comes back 422.

Program.cs
using Mailblastr;

var apiKey = Environment.GetEnvironmentVariable("MAILBLASTR_API_KEY")
             ?? throw new InvalidOperationException("MAILBLASTR_API_KEY is not set");

IMailblastr mailblastr = MailblastrClient.Create(apiKey);

try
{
    EmailCreated sent = await mailblastr.EmailSendAsync(new EmailMessage
    {
        From = "Acme <hello@yourdomain.com>",
        To = "delivered@mailblastr.dev",
        Subject = "Hello from .NET",
        HtmlBody = "<p>Sent with the official Mailblastr .NET SDK.</p>",
    });

    Console.WriteLine("Sent email " + sent.Id);
}
catch (MailblastrException ex)
{
    // The API error envelope: { statusCode, name, message }.
    Console.Error.WriteLine(ex.StatusCode + " " + ex.Name + ": " + ex.Message);
}

Two property names differ from the JSON they serialize to, because C# reserves nothing useful here: the HTML body is HtmlBody (sent as html) and the plain-text alternative is TextBody (sent as text). To, Cc, Bcc and ReplyTo are EmailAddressList, which converts implicitly from a single string or a string[], so both of these compile:

To = "delivered@mailblastr.dev",
To = new[] { "delivered@mailblastr.dev", "bounced@mailblastr.dev" },

Handling the response

A successful send resolves to EmailCreated, whose single Id property is the new email id. Keep it to retrieve the email later or to correlate webhook events.

{
  "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
}

Any non-2xx answer throws MailblastrException carrying the envelope as StatusCode, Name and Message. Branch on Name together with StatusCode, never on Message — message text is scrubbed of provider identifiers server-side and is not a stable contract. Plan and quota rejections additionally populate Limit, reputation gates populate Reputation, and a batch that failed part way through populates Sent/SentCount; all of them are null on an ordinary error.

catch (MailblastrException ex)
{
    switch (ex.Name)
    {
        case "validation_error":
            // 422 for a bad field — or 403 when the User-Agent header is missing.
            break;
        case "daily_quota_exceeded":
            if (ex.Limit is not null)
            {
                Console.Error.WriteLine(ex.Limit.Kind + " cap hit: "
                    + ex.Limit.Used + "/" + ex.Limit.Limit);
            }
            break;
    }
}
The SDK sends User-Agent: mailblastr-dotnet/5.0.0 on every request. The API rejects a request with a missing or blank User-Agent with 403 validation_error before it even authenticates, which is why raw-HttpClient callers have to set the header themselves and SDK callers do not.

Dependency injection and tuning

Pass MailblastrClientOptions to supply your own HttpClient — typically one from IHttpClientFactory — or to change the timeout and retry budget. A client you supply is never disposed by the SDK, and it needs no headers of its own: the Authorization and User-Agent headers are set per request.

Program.cs (ASP.NET Core)
builder.Services.AddHttpClient("mailblastr");

builder.Services.AddSingleton<IMailblastr>(sp =>
    MailblastrClient.Create(
        builder.Configuration["Mailblastr:ApiKey"]!,
        new MailblastrClientOptions
        {
            HttpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient("mailblastr"),
            Timeout = TimeSpan.FromSeconds(10),
            MaxRetries = 3,
        }));

Timeout bounds each individual attempt — a retry gets a fresh one — and defaults to 30 seconds; TimeSpan.Zero disables it. MaxRetries (default 2) is the budget for 429 and 503 responses only. No other status, no network error and no timeout is retried, so a send is never silently duplicated. When you supply your own HttpClient, remember its own HttpClient.Timeout still applies on top.

Scheduling a send

Set ScheduledAt to an ISO 8601 timestamp to queue the email instead of sending it now. Scheduled sends bypass the delivery simulator, so use a real address on your own verified domain here — delivered@mailblastr.dev is only honoured for immediate sends and would be rejected.

var sent = await mailblastr.EmailSendAsync(new EmailMessage
{
    From = "Acme <hello@yourdomain.com>",
    To = "you@yourdomain.com",
    Subject = "Your weekly digest",
    HtmlBody = "<p>Scheduled ahead of time.</p>",
    ScheduledAt = "2026-09-01T09:00:00Z",
});

// Later: reschedule or cancel by id.
await mailblastr.EmailUpdateAsync(sent.Id, "2026-09-02T09:00:00Z");
await mailblastr.EmailCancelAsync(sent.Id);
Read the mb_ API key from configuration or your secrets manager and keep it server-side. Never ship it in a desktop, mobile, or Blazor WebAssembly client — the key can send email as your account.

Next steps

  • Send up to 100 messages in one request with EmailBatchAsync, passing a list of BatchEmailMessage. Batch items reject Attachments and ScheduledAt — send those one at a time with EmailSendAsync.
  • Pass an idempotencyKey to EmailSendAsync to make a retry safe; replaying the key returns the original result. It is honoured by the send and batch-send routes only.
  • See the full Send Email API reference for every body field (Cc, Bcc, ReplyTo, Attachments, Headers, TemplateId).
  • Verify inbound webhooks with WebhookSignature.Verify(rawBody, headers, signingSecret) — pass the raw request body, never re-serialized JSON. See Webhooks.
  • Explore the SDKs reference for the other resources the package exposes — contacts, segments, topics, campaigns, templates, automations, domains and events.
  • Prefer no package at all? Send with .NET over raw HTTP uses only IHttpClientFactory.