Sendhiiv 0.3.1

dotnet add package Sendhiiv --version 0.3.1
                    
NuGet\Install-Package Sendhiiv -Version 0.3.1
                    
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="Sendhiiv" Version="0.3.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sendhiiv" Version="0.3.1" />
                    
Directory.Packages.props
<PackageReference Include="Sendhiiv" />
                    
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 Sendhiiv --version 0.3.1
                    
#r "nuget: Sendhiiv, 0.3.1"
                    
#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 Sendhiiv@0.3.1
                    
#: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=Sendhiiv&version=0.3.1
                    
Install as a Cake Addin
#tool nuget:?package=Sendhiiv&version=0.3.1
                    
Install as a Cake Tool

Sendhiiv .NET SDK

Official .NET client for the Sendhiiv email API. On .NET Framework the package has zero dependencies — installing it never adds, upgrades, or conflicts with Newtonsoft.Json or anything else in your project. Modern targets use System.Text.Json.

Supported runtimes

Your project Supported Build NuGet installs
.NET 10, 9, 8 yes net8.0
.NET 6, 7, .NET Core 2.0+ yes netstandard2.0
.NET Framework 4.5 – 4.8 yes net45 (no dependencies)
Mono, Xamarin, Unity yes netstandard2.0

The package ships three builds and NuGet picks the right one for you. .NET 9, 10, and future releases run the net8.0 build — newer .NET runtimes load assemblies built for earlier versions, which is why the package page lists only the compiled targets while remaining compatible with everything above.

dotnet add package Sendhiiv

or in the NuGet Package Manager Console: Install-Package Sendhiiv

Quickstart

using Sendhiiv;

var sendhiiv = new SendhiivClient(Environment.GetEnvironmentVariable("SENDHIIV_API_KEY"));

var result = await sendhiiv.Messages.SendAsync(new SendMessageParams
{
    From = "Acme <hello@yourdomain.com>",
    To = { "customer@example.com" },
    Subject = "Welcome aboard",
    Html = "<p>Hi there, your account is ready.</p>",
});

Console.WriteLine(result.Message); // "1 email(s) queued for delivery"

Get an API key from your Sendhiiv dashboard under Settings → API. The free tier includes 3,000 emails/month. Keys look like sh_live_... — keep them in an environment variable or user secrets, not in code.

How the SDK is organized

There are only a handful of public types, and they map one-to-one onto the API:

Type What it's for
SendhiivClient Entry point. Create one and reuse it for the life of your app.
SendhiivClient.Messages The messages resource. Messages.SendAsync(...) is the send call.
SendhiivClientOptions Optional constructor settings: timeout, retries, base URL, your own HttpClient.
SendMessageParams The email you want to send. Plain object, all the fields are below.
Attachment One file attached to a message.
SendMessageResponse What a successful send returns (the API answers 202 Accepted).
SendhiivException Thrown for any non-2xx response, network failure, or timeout.
ComplianceInfo Extra detail on SendhiivException when content was blocked by review.

The API currently has one endpoint (POST /messages), so Messages.SendAsync is the only method you'll call. Everything else is data going in or coming out of it. Resources are grouped the way you'd expect from Stripe-style clients, so when more endpoints ship they'll appear as new properties on the client (sendhiiv.Domains, etc.) without breaking anything.

SendhiivClient is thread-safe and holds a single HttpClient internally. Make one instance and share it; don't create a client per request.

SendMessageParams, field by field

Property JSON field Required Notes
To to yes One or many recipient addresses. One request with 200 recipients is cheaper than 200 requests.
Subject subject usually Can only be omitted when a message template supplies its own subject.
Html html see below HTML body. When combined with a layout template, this content is placed inside the layout.
Text text see below Plain-text body.
TemplateKey template_key see below Key of a saved layout or message template, e.g. "brand-layout". Template keys are listed on the Templates page of the dashboard.
From from no Display sender, e.g. "Acme <hello@yourdomain.com>". The domain must be verified in your account. Omit it to send from the shared sender.
ReplyTo reply_to no Reply-To address.
Variables variables no Dictionary of values for {{merge}} tags in the subject, body, or template. Variables["firstName"] = "Ada" fills {{firstName}}.
Attachments attachments no List of Attachment. 10 MB total per message.
SendMode send_mode no Set to "drip" to schedule recipients in batches instead of sending all at once.
BatchSize batch_size no Drip only. Recipients per batch, default 50, max 500.
BatchIntervalMinutes batch_interval_minutes no Drip only. Minutes between batches, default 15, max 1440.

The one rule to remember: every message needs To plus at least one of Html, Text, or TemplateKey. The rest is optional.

The serializer skips null properties, so you only set what you use — an unset property is simply absent from the request.

A complete console app

Starting from nothing:

dotnet new console -n EmailDemo
cd EmailDemo
dotnet add package Sendhiiv

Program.cs:

using Sendhiiv;

var apiKey = Environment.GetEnvironmentVariable("SENDHIIV_API_KEY")
    ?? throw new InvalidOperationException("Set SENDHIIV_API_KEY first.");

var sendhiiv = new SendhiivClient(apiKey);

try
{
    var result = await sendhiiv.Messages.SendAsync(new SendMessageParams
    {
        To = { "you@example.com" },
        Subject = "Hello from the SDK",
        Html = "<p>It works.</p>",
    });

    Console.WriteLine($"{result.Status}: {result.Message} ({result.Total} recipient(s))");
}
catch (SendhiivException ex)
{
    Console.Error.WriteLine($"Send failed (HTTP {ex.Status}, code {ex.Code}): {ex.Message}");
}

Run it:

set SENDHIIV_API_KEY=sh_live_...     (Windows)
export SENDHIIV_API_KEY=sh_live_...  (macOS/Linux)
dotnet run

Note there's no From in that example — without a verified domain the message goes out via the shared sender, which is fine for trying things out.

Using it in ASP.NET Core

Register the client once as a singleton. If you use IHttpClientFactory, hand its client to the SDK so your existing pooling and logging apply:

// Program.cs
builder.Services.AddHttpClient("sendhiiv");
builder.Services.AddSingleton(sp =>
{
    var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient("sendhiiv");
    return new SendhiivClient(
        builder.Configuration["Sendhiiv:ApiKey"]!,
        new SendhiivClientOptions { HttpClient = httpClient });
});

with the key in appsettings.json / user secrets / environment:

{ "Sendhiiv": { "ApiKey": "sh_live_..." } }

Then inject it wherever you send mail:

public class SignupService
{
    private readonly SendhiivClient _sendhiiv;

    public SignupService(SendhiivClient sendhiiv) => _sendhiiv = sendhiiv;

    public async Task SendWelcomeAsync(string email, string firstName, CancellationToken ct)
    {
        await _sendhiiv.Messages.SendAsync(new SendMessageParams
        {
            From = "Acme <hello@yourdomain.com>",
            To = { email },
            Subject = "Welcome to Acme",
            TemplateKey = "welcome-email",
            Variables = new Dictionary<string, object> { ["firstName"] = firstName },
        }, ct);
    }
}

SendAsync takes an optional CancellationToken, so request-aborted cancellation flows through naturally.

The SDK never disposes an HttpClient you pass in — lifetime stays yours.

.NET Framework

Nothing special is required — Install-Package Sendhiiv works in Framework projects from 4.5 up, including old ASP.NET MVC / Web API / WebForms apps. Every Framework project (4.5 through 4.8) gets the dedicated net45 build.

Details worth knowing on old Framework versions:

  • Zero dependencies, on purpose. Legacy apps break when a package forces a Newtonsoft.Json upgrade, so the Framework build doesn't use Newtonsoft at all — it serializes with the framework's built-in serializer (System.Web.Extensions, already on every machine). Installing Sendhiiv adds exactly one entry to your packages.config: Sendhiiv. Whatever JSON library your app uses, at whatever version, stays untouched.
  • TLS is handled for you. .NET 4.5 negotiates TLS 1.0 by default, which the API rejects. The Framework build enables TLS 1.2 itself the first time you create a SendhiivClient, so sends work without touching ServicePointManager or the registry.
  • Synchronous code is fine. All calls are async, but .GetAwaiter().GetResult() won't deadlock on the ASP.NET/WinForms synchronization context, because the SDK awaits internally with ConfigureAwait(false).

The test suite runs against both the modern build and the net45 build on the classic CLR, so the Framework path is exercised on every change, not just compiled.

"Could not load file or assembly 'Newtonsoft.Json'"

If you see Could not load file or assembly 'Newtonsoft.Json, Version=...' (HRESULT 0x80131040) after installing, you're on an SDK version older than 0.3.0 — those briefly depended on Newtonsoft. Update the package (Update-Package Sendhiiv) and the error is gone for good: 0.3.0+ has no Newtonsoft reference at all. If the old SDK version upgraded Newtonsoft in your project and that upgrade caused problems elsewhere, you're free to roll Newtonsoft back to the version you had — this package no longer cares.

What a successful send returns

The API queues messages and answers 202 Accepted. SendMessageResponse looks like this on the wire:

{
  "success": true,
  "status": "queued",
  "code": "QUEUED_FOR_DELIVERY",
  "message": "1 email(s) queued for delivery",
  "total": 1,
  "retry": { "automatic": true, "retryable_temporary_failures": true }
}
Property Meaning
Success true on 202.
Status "queued".
Code "QUEUED_FOR_DELIVERY".
Message Human-readable summary.
Total Number of recipients queued.
Retry Server-side behavior: Sendhiiv retries temporary delivery failures itself after queueing.

Queued means accepted for delivery, not delivered — delivery status shows up in your dashboard's activity log.

Error handling

Every non-2xx response throws a SendhiivException. It carries:

Property Meaning
Status HTTP status code. 0 for network errors and timeouts.
Code Machine-readable code such as "QUOTA_EXCEEDED", or null when the API didn't send one.
Compliance Score, severity, and reasons — only set when Code is "CONTENT_COMPLIANCE_BLOCKED".
ResponseBody The raw response body, when one was received. Useful for logging.
Message The API's error text, or a description of the network failure.
try
{
    await sendhiiv.Messages.SendAsync(message);
}
catch (SendhiivException ex)
{
    switch (ex.Code)
    {
        case "CONTENT_COMPLIANCE_BLOCKED":
            logger.LogWarning("Blocked: {Reasons}", string.Join("; ", ex.Compliance?.Reasons ?? new()));
            break;
        case "QUOTA_EXCEEDED":       // 429 — monthly plan quota reached
        case "ATTACHMENT_TOO_LARGE": // 413 — 10 MB total limit
        default:
            logger.LogError("Sendhiiv HTTP {Status}: {Message}", ex.Status, ex.Message);
            break;
    }
}
Status Meaning
202 Accepted — message(s) queued for delivery
400 Invalid request (missing to, bad attachments, content blocked by compliance review — check Code)
401 Missing, invalid, or revoked API key
402 Pay-as-you-go balance exhausted
403 Plan does not include API access
413 Attachments exceed 10 MB total
429 Rate limit (100 requests/min) or plan quota reached

Retries and timeouts

The SDK retries only HTTP 429 responses (honoring Retry-After), because the rate limiter runs before anything is queued — a retry can never double-send. Network errors and 5xx responses are not retried automatically, since the message may already have been accepted. Sendhiiv itself retries temporary delivery failures server-side after a message is queued.

All the knobs live on SendhiivClientOptions:

var sendhiiv = new SendhiivClient(apiKey, new SendhiivClientOptions
{
    Timeout = TimeSpan.FromSeconds(30), // per-request timeout (default 30s)
    MaxRetries = 2,                     // 429 retries (default 2)
    HttpClient = httpClientFromFactory, // optional: bring your own HttpClient
    BaseUrl = "https://api.sendhiiv.com/api/v1", // default; override for testing
});

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net45 is compatible.  net451 was computed.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.5

    • No dependencies.
  • .NETStandard 2.0

  • 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
0.3.1 90 7/20/2026
0.3.0 108 7/15/2026
0.2.2 90 7/15/2026
0.2.1 97 7/8/2026
0.2.0 93 7/8/2026
0.1.0 103 7/8/2026