MisarMail 5.0.3

dotnet add package MisarMail --version 5.0.3
                    
NuGet\Install-Package MisarMail -Version 5.0.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MisarMail" Version="5.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MisarMail" Version="5.0.3" />
                    
Directory.Packages.props
<PackageReference Include="MisarMail" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MisarMail --version 5.0.3
                    
#r "nuget: MisarMail, 5.0.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MisarMail@5.0.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MisarMail&version=5.0.3
                    
Install as a Cake Addin
#tool nuget:?package=MisarMail&version=5.0.3
                    
Install as a Cake Tool

MisarMail .NET SDK

Send transactional email and run marketing campaigns from C# — async/await, cancellation, no dependencies.

NuGet net license

33 resource groups · 91 methods · SSE streaming · no dependencies beyond the BCL

MisarMail is one API for both halves of your email: the receipts and password resets your product sends, and the campaigns, segments and automations your marketing team runs on the same contact list and the same verified domains.

Targets net8.0. The surface is flat — Group_MethodAsync, not nested resource objects — and every method takes an optional CancellationToken and returns Task<JsonElement>.


Install

dotnet CLI

dotnet add package MisarMail --version 5.0.3

PackageReference

<PackageReference Include="MisarMail" Version="5.0.3" />

Package Manager

Install-Package MisarMail -Version 5.0.3

Authentication

Create a developer key at https://mail.misar.io/developers. It starts with msk_ and is sent as Authorization: Bearer msk_….

Every call is metered against the subscription attached to that key. There is no client-side limit checking — the server decides, and the SDK surfaces its answer. A plan refusal answers 403 with code: "plan_limit_exceeded" and is never retried.

using MisarMail;

using var mail = new MisarMailClient(Environment.GetEnvironmentVariable("MISARMAIL_API_KEY")!);

Resources

The surface is flat: you call mail.Group_MethodAsync(…). The Methods column lists the middle segment — Email + Send is mail.Email_SendAsync.

Send

Resource Methods What it covers
Email_…Async Send Transactional send — cc/bcc/reply-to, tags, metadata, idempotency_key.
Sandbox_…Async Send, List, Delete Test sends captured instead of delivered.

Campaigns and tests

Resource Methods What it covers
Campaigns_…Async List, Create, Get, Update, SendCampaign, Delete Marketing campaigns: draft, edit, queue for send.
AbTests_…Async List, Create, Get, SetWinner Subject, content, send-time, from-name and preheader splits, and winner selection.

Audience

Resource Methods What it covers
Contacts_…Async List, Create, Get, Update, Delete, ImportContacts Subscribers, plus bulk import.
Segments_…Async Members Dynamic audience segments and their membership.
LandingPages_…Async Create Hosted landing pages with an email capture form.

Content

Resource Methods What it covers
Templates_…Async List, Create, Get, Update, Delete, Render Reusable templates and server-side variable rendering.
Ai_…Async SubjectLines AI-generated subject lines.

Automations

Resource Methods What it covers
Automations_…Async List, Create, Get, Update, Delete, Activate Trigger-based workflows — welcome series, drips, re-engagement.

Deliverability and sending infrastructure

Resource Methods What it covers
Domains_…Async List, Create, Get, Verify, Delete Sending domains and their DNS verification.
Dmarc_…Async Check, ListDomains, AddDomain, RemoveDomain Live SPF/DKIM/DMARC record checks and monitored domains.
Deliverability_…Async Audit, Score Deliverability score, audit and remediation guidance.
DedicatedIPs_…Async List, Create, Update, Delete Dedicated sending IPs.
Warmup_…Async Get IP/domain warm-up progress and today's remaining capacity.
Inbound_…Async List, Create, Get, Delete Inbound routing domains, so replies land in the unified inbox.

Mailbox and inbox

Resource Methods What it covers
Emails_…Async List, Get, Update Stored messages in the mailbox.
EmailAccounts_…Async List Connected mailbox accounts.

Analytics and attribution

Resource Methods What it covers
Analytics_…Async Overview Delivery and engagement stats — aggregate, or one campaign.
Track_…Async Event, Purchase Custom events and ecommerce purchases.
Revenue_…Async Attribution Revenue attributed back to email.
Usage_…Async Get Metered usage for a period.

Validation

Resource Methods What it covers
Validate_…Async Email Address validation, and the credit balance behind it.

Plan, billing and credits

Resource Methods What it covers
Plan_…Async Get, Monetization, Limits Current plan, quotas and monetization stats.
Billing_…Async Subscription, Checkout Subscription state and checkout.
Subscription_…Async Get, Upsert, Cancel Subscription read/write and per-product plan limits.
Wallet_…Async Get, Credit, Debit Credit balance, credit and debit.
CreditRates_…Async List What each metered action costs in credits.
TeamMembers_…Async Get Team members on the account.
Monetization_…Async Tip Newsletter tips.

Developer

Resource Methods What it covers
Keys_…Async List, Create, Get, Revoke API keys — create, list, revoke.
Webhooks_…Async List, Create, Get, Update, Delete, Test Webhook endpoints, plus a test delivery.
Streaming_…Async GenerateEmail, CampaignSend The two Server-Sent Events endpoints.

Client

Thing Detail
Entry point new MisarMailClient(apiKey, baseUrl, maxRetries, httpClient). Implements IDisposable and owns its HttpClient unless you pass one.
Defaults https://api.misar.io/mail/v1, maxRetries 3, a fresh HttpClient with a 30-second timeout.
Validation A blank apiKey throws ArgumentException immediately.
Payloads Any object, serialized with System.Text.Json — anonymous objects work as-is.
Results Task<JsonElement>; read with GetProperty or deserialize into your own records.
Cancellation Every method takes an optional CancellationToken.
Retried 429, 500, 502, 503, 504, HttpRequestException and TaskCanceledException — 500 ms then 1 s.
Never retried Plan refusals, and streams.
Errors MisarMailException, MisarMailNetworkException, MisarMailPlanLimitException.
Webhook verifier Not shipped here — verify HMAC-SHA256(timestamp + "." + rawBody) yourself with HMACSHA256 and CryptographicOperations.FixedTimeEquals. (Go, Python, Ruby and Dart ship one.)

Quick start

using MisarMail;

using var mail = new MisarMailClient(Environment.GetEnvironmentVariable("MISARMAIL_API_KEY")!);

var sent = await mail.Email_SendAsync(new {
    from = new { email = "you@yourdomain.com", name = "Your App" },
    to = new[] { new { email = "someone@example.com" } },
    subject = "Hello",
    html = "<p>Hi there</p>",
});

Console.WriteLine(sent.GetProperty("message_id").GetString());

Primary functions

Send a transactional email

from is a single address object and to is an array of them. Pass an idempotency_key and a retry can never send twice — the response comes back with idempotent: true the second time.

var res = await mail.Email_SendAsync(new {
    from = new { email = "receipts@yourdomain.com", name = "Acme" },
    to = new[] { new { email = "customer@example.com" } },
    reply_to = new { email = "support@yourdomain.com" },
    subject = "Your receipt",
    html = "<p>Thanks for your order.</p>",
    text = "Thanks for your order.",
    tags = new[] { "receipt" },
    metadata = new { order_id = "ord-1041" },
    idempotency_key = "ord-1041-receipt",
});

res.GetProperty("message_id").GetString();   // "msg-…"

List and create contacts

Responses are enveloped. Contacts_ListAsync returns { success, data, pagination } and takes the filters as a raw query string; Contacts_CreateAsync returns { success, data } with the contact under data.

var page = await mail.Contacts_ListAsync("page=1&limit=50&status=subscribed");
Console.WriteLine($"{page.GetProperty("data").GetArrayLength()} of {page.GetProperty("pagination").GetProperty("total")}");

var created = await mail.Contacts_CreateAsync(new {
    email = "new@example.com",
    firstName = "Ada",
    lastName = "Lovelace",
    tags = new[] { "beta" },
    customFields = new { plan = "pro" },
});

Console.WriteLine(created.GetProperty("data").GetProperty("id").GetString());

Contacts_GetAsync and Contacts_DeleteAsync take the contact id, which the route reads from the query string rather than a path segment. Contacts_UpdateAsync is different again: it identifies the contact by email address, not by id, and merges that address into whatever payload you pass.

await mail.Contacts_UpdateAsync("ada@example.com", new { status = "unsubscribed" });

Bulk import contacts

Counts come back under summary, and errors is a separate list of messages.

var imported = await mail.Contacts_ImportContactsAsync(new {
    contacts = new[] {
        new { email = "a@example.com", firstName = "A" },
        new { email = "b@example.com", firstName = "B" },
    },
    updateExisting = true,
});

imported.GetProperty("summary");   // { imported, updated, skipped, errors }
imported.GetProperty("errors");    // array of strings

Create and send a campaign

Campaigns take fromName and fromEmail as separate fields — there is no from object here, unlike Email_SendAsync. Campaigns_SendCampaignAsync queues it and reports it as scheduled.

var campaign = await mail.Campaigns_CreateAsync(new {
    name = "March launch",
    subject = "We just shipped",
    fromName = "Ada at Acme",
    fromEmail = "hello@yourdomain.com",
    replyTo = "support@yourdomain.com",
    bodyHtml = "<h1>It's live</h1>",
    segmentId = "seg-123",
});

string id = campaign.GetProperty("data").GetProperty("id").GetString()!;
var queued = await mail.Campaigns_SendCampaignAsync(id);
Console.WriteLine($"{queued.GetProperty("campaignId")} {queued.GetProperty("status")}");  // … scheduled

Only draft, scheduled or paused campaigns can be updated, and only draft campaigns can be deleted.

Validate an address

Each call spends a credit, and the response tells you what is left.

var check = await mail.Validate_EmailAsync("someone@example.com");
var verdict = check.GetProperty("data");

verdict.GetProperty("is_valid").GetBoolean();
verdict.GetProperty("score").GetDouble();                       // 0–1 confidence
verdict.GetProperty("checks");                                  // { syntax, mx, smtp }
verdict.GetProperty("flags").GetProperty("disposable").GetBoolean();
check.GetProperty("credits").GetProperty("balance_after").GetInt32();

Render a template

var rendered = await mail.Templates_RenderAsync(new {
    template_id = "tpl-123",
    variables = new { name = "Ada", plan = "Pro" },
});

rendered.GetProperty("data").GetProperty("subject").GetString();   // "Welcome, Ada"
rendered.GetProperty("data").GetProperty("html");

Track events and revenue

The event name field is event_name, and purchase totals are integer cents in total_cents.

await mail.Track_EventAsync(new {
    email = "customer@example.com",
    event_name = "viewed_pricing",
    event_data = new { plan = "pro" },
});

var purchase = await mail.Track_PurchaseAsync(new {
    email = "customer@example.com",
    order_id = "ord-1041",
    total_cents = 9900,
    currency = "USD",
    items = new[] { new { name = "Pro annual", quantity = 1, price_cents = 9900 } },
});

purchase.GetProperty("attribution");   // which campaign or automation earned it

Read analytics and manage keys

Without campaignId you get aggregate usage and totals for the period; with one you get that campaign's stats and rates. Keys_ListAsync returns the keys under keys, not data, and Keys_CreateAsync returns the raw key exactly once.

var overall = await mail.Analytics_OverviewAsync("startDate=2026-04-01&endDate=2026-04-30");
var one = await mail.Analytics_OverviewAsync($"campaignId={id}");

var keys = await mail.Keys_ListAsync();
keys.GetProperty("keys").GetArrayLength();

var fresh = await mail.Keys_CreateAsync(new { name = "CI", scopes = new[] { "send", "read" } });
fresh.GetProperty("key").GetString();   // shown once and never again

Errors

Three exception types, all in the MisarMail namespace:

Type When
MisarMailException Any non-2xx API response. Carries Status.
MisarMailNetworkException The request never got an answer, or every retry was spent. Status is 0.
MisarMailPlanLimitException The subscription behind the key does not cover the call.

Both narrower types derive from MisarMailException, so catch (MisarMailException) catches everything the SDK throws. The base type exposes three properties so you don't have to compare status codes by hand: IsUnauthorized (401), IsPlanDenied (402/403/429) and IsRetryable (429 or 5xx).

Plan limits

Both a spent allowance and a feature that is not on the plan answer 403, carrying code: "plan_limit_exceeded". The SDK keys on that code rather than the status, which is why a refusal is typed correctly even though 403 is otherwise an authorization failure. It throws MisarMailPlanLimitException and does not retry — retrying cannot help until the allowance resets or the plan changes. Read UpgradeUrl to send the user somewhere useful.

try {
    await mail.Campaigns_CreateAsync(new {
        name = "Blast", subject = "We just shipped",
        fromName = "Your Name", fromEmail = "you@yourdomain.com",
    });
} catch (MisarMailPlanLimitException e) {
    Console.Error.WriteLine($"{e.Feature} exhausted on {e.Plan}: {e.UpgradeUrl} (retry after {e.RetryAfter})");
}

Plan_GetAsync returns plan, sending (the per-day and per-month email caps), usage — an array with one entry per metered feature, each carrying used, limit and remaining — and upgrade, which is null until a quota is tight. A null limit means unlimited, and remaining is null alongside it rather than 0. Read it before an expensive call rather than discovering the ceiling through a refusal.

The key needs the read or subscription scope.

var plan = await mail.Plan_GetAsync();
Console.WriteLine($"{plan.GetProperty("sending")} {plan.GetProperty("usage")}");

Streaming

Two endpoints stream Server-Sent Events. Both sit outside /v1, which the SDK handles for you:

Method Route
Streaming_GenerateEmailAsync POST /api/ai/generate-email/stream
Streaming_CampaignSendAsync GET /api/campaigns/{id}/send-stream

Frames are unnamed (data: {…}, with no event: line) and the stream ends with data: [DONE], which the SDK consumes rather than handing on. Each MisarMailStreamEvent carries Event (normally null), Data (the parsed JsonElement?) and Raw (the payload exactly as received). A stream is never retried: replaying one that failed mid-flight would duplicate whatever you had already read.

await foreach (var frame in mail.Streaming_GenerateEmailAsync(new { prompt = "a launch email" }))
    Console.Write(frame.Data?.GetProperty("delta").GetString());

await foreach (var frame in mail.Streaming_CampaignSendAsync(id))
    Console.WriteLine(frame.Raw);

MIT © Misar AI

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.0.3 104 8/18/2026
5.0.2 96 8/18/2026
5.0.1 104 8/18/2026
5.0.0 105 8/18/2026
1.0.0 110 8/18/2026