Emailit.Client.v2
2.1.2
dotnet add package Emailit.Client.v2 --version 2.1.2
NuGet\Install-Package Emailit.Client.v2 -Version 2.1.2
<PackageReference Include="Emailit.Client.v2" Version="2.1.2" />
<PackageVersion Include="Emailit.Client.v2" Version="2.1.2" />
<PackageReference Include="Emailit.Client.v2" />
paket add Emailit.Client.v2 --version 2.1.2
#r "nuget: Emailit.Client.v2, 2.1.2"
#:package Emailit.Client.v2@2.1.2
#addin nuget:?package=Emailit.Client.v2&version=2.1.2
#tool nuget:?package=Emailit.Client.v2&version=2.1.2
Emailit.Client v2
A .NET client library for the Emailit API v2, built on top of Flurl HTTP.
The client covers emails, domains, API keys, audiences, subscribers, templates, suppressions, email verification, contacts, events, and webhooks.
Current package version prepared in this repository: 2.1.2.
Overview
The library provides:
- async access to the Emailit v2 API through typed request and response models
- dependency injection support and a multi-tenant client factory
- typed exceptions with normalized
ProblemDetailsconversion - rate limit metadata on responses and on the client instance
- webhook signature verification helpers
- compatibility handling for inconsistent production payload shapes
The client is tolerant of several response variants seen in production, including:
- booleans returned as
true/false,0/1, or strings - numeric fields returned as strings
- timestamps returned in mixed formats or as empty strings
- recipient fields returned as either a single string or an array
- attachment payloads returned under either
attachmentsordata - legacy resend behavior routed through retry when
/resendis unavailable
Installation
dotnet add package Emailit.Client.v2
Target frameworks:
- .NET 8.0
- .NET 9.0
- .NET 10.0
Configuration
appsettings.json
{
"Emailit": {
"ApiKey": "em_your_api_key_here",
"BaseUrl": "https://api.emailit.com",
"TimeoutSeconds": 30,
"ExceptionDetailMode": "Safe",
"MaxDiagnosticBodyLength": 1024
}
}
using Emailit.Client.DependencyInjection;
builder.Services.AddEmailitClient(builder.Configuration);
Manual configuration
using var client = new EmailitClient(new EmailitClientOptions
{
ApiKey = "em_your_api_key_here",
BaseUrl = "https://api.emailit.com",
TimeoutSeconds = 30,
ExceptionDetailMode = EmailitExceptionDetailMode.Safe,
MaxDiagnosticBodyLength = 1024
});
ExceptionDetailMode controls how much context is captured in thrown exceptions:
Minimal- status, error code, rate limits, and transient/retry metadataSafe-Minimalplus request method, request URI without query string, and request ID when availableDiagnostic-Safeplus truncated response body and response headers
Multi-tenant factory
builder.Services.AddEmailitClientFactory(options =>
{
options.BaseUrl = "https://api.emailit.com";
options.TimeoutSeconds = 30;
});
Common Usage
Typical flow:
- configure
EmailitClientOptions - inject
IEmailitClientor createEmailitClientmanually - call a typed async method such as
SendEmailAsync,ListDomainsAsync, orVerifyEmailAsync - handle typed
EmailitExceptionsubclasses or convert them toProblemDetails
Send an email
var response = await client.SendEmailAsync(new SendEmailRequest
{
From = "sender@yourdomain.com",
To = ["recipient@example.com"],
Subject = "Hello from Emailit",
Html = "<h1>Welcome</h1><p>This is a test email.</p>"
});
Console.WriteLine($"{response.Id} - {response.Status}");
Send with template
var response = await client.SendEmailAsync(new SendEmailRequest
{
From = "sender@yourdomain.com",
To = ["recipient@example.com"],
TemplateId = "tpl_abc123",
Variables = new Dictionary<string, object>
{
["first_name"] = "John"
}
});
Schedule, update, and cancel
var scheduled = await client.SendEmailAsync(new SendEmailRequest
{
From = "sender@yourdomain.com",
To = ["recipient@example.com"],
Subject = "Scheduled email",
Html = "<p>Scheduled</p>",
ScheduledAt = "2026-12-25T10:00:00Z"
});
await client.UpdateScheduledEmailAsync(scheduled.Id, new UpdateScheduledEmailRequest
{
ScheduledAt = "2026-12-26T10:00:00Z"
});
await client.CancelEmailAsync(scheduled.Id);
Email sub-resources
var meta = await client.GetEmailMetaAsync("em_abc123");
var body = await client.GetEmailBodyAsync("em_abc123");
var raw = await client.GetEmailRawAsync("em_abc123");
var attachments = await client.GetEmailAttachmentsAsync("em_abc123");
Retry a failed email
var retried = await client.RetryEmailAsync("em_failed123");
Console.WriteLine($"{retried.Id} - {retried.Status}");
Verify a single email
var verification = await client.VerifyEmailAsync(new VerifyEmailRequest
{
Email = "user@example.com"
});
Console.WriteLine($"{verification.Email} - {verification.Result}");
Manage domains
var domain = await client.CreateDomainAsync(new CreateDomainRequest
{
Name = "yourdomain.com",
TrackLoads = true,
TrackClicks = true
});
await client.UpdateDomainAsync(domain.Id, new UpdateDomainRequest
{
TrackLoads = true,
TrackClicks = false
});
await client.VerifyDomainAsync(domain.Id);
Note:
UpdateDomainAsyncusesPOSTagainst the production API.
Manage audiences and subscribers
var audience = await client.CreateAudienceAsync(new CreateAudienceRequest
{
Name = "Newsletter"
});
var subscriber = await client.AddSubscriberAsync(audience.Id, new AddSubscriberRequest
{
Email = "john@example.com",
FirstName = "John",
LastName = "Doe"
});
Manage templates
var template = await client.CreateTemplateAsync(new CreateTemplateRequest
{
Name = "Welcome Email",
Alias = "welcome-email",
Subject = "Welcome",
From = "noreply@yourdomain.com",
Html = "<h1>Hello</h1>",
Editor = "html"
});
await client.PublishTemplateAsync(template.Id);
Manage contacts
var contact = await client.CreateContactAsync(new CreateContactRequest
{
Email = "user@example.com",
FirstName = "John",
LastName = "Doe"
});
await client.UpdateContactAsync(contact.Id, new UpdateContactRequest
{
Unsubscribed = true
});
Manage webhooks
var webhook = await client.CreateWebhookAsync(new CreateWebhookRequest
{
Name = "Production Webhook",
Url = "https://yourapp.com/webhooks/emailit",
AllEvents = true
});
Verify webhook signatures
using Emailit.Client.Webhooks;
var isValid = WebhookSignatureValidator.ValidateSignature(
payload,
signature,
timestamp,
webhookSecret,
clockTolerance: TimeSpan.FromMinutes(5));
Error Handling
using Emailit.Client.Exceptions;
try
{
await client.SendEmailAsync(request);
}
catch (EmailitValidationException ex)
{
var problem = ex.ToProblemDetails();
return Results.ValidationProblem(
ex.ValidationErrors?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? new Dictionary<string, string[]>(),
detail: problem.Detail,
title: problem.Title,
type: problem.Type);
}
catch (RateLimitExceededException ex)
{
var problem = ex.ToProblemDetails();
return Results.Problem(
title: problem.Title,
type: problem.Type,
detail: problem.Detail,
statusCode: problem.Status,
extensions: new Dictionary<string, object?>(problem.Extensions));
}
catch (EmailitTimeoutException ex) when (ex.IsTransient)
{
throw;
}
catch (EmailitException ex)
{
var problem = ex.ToProblemDetails();
return Results.Problem(
title: problem.Title,
type: problem.Type,
detail: problem.Detail,
statusCode: problem.Status ?? 502,
extensions: new Dictionary<string, object?>(problem.Extensions));
}
The client classifies failures into distinct exception types so your application can make decisions instead of only logging messages:
EmailitValidationException- invalid request payloadEmailitUnprocessableEntityException- request shape is valid but the current resource state makes the operation invalidEmailitAuthenticationException/EmailitAuthorizationException- credential or permission issuesEmailitNotFoundException/EmailitConflictException- resource state issuesRateLimitExceededException/DailyLimitExceededException- retry or backoff conditionsEmailitTimeoutException/EmailitTransportException- transient network failuresEmailitSerializationException/EmailitUnexpectedResponseException- contract mismatches between the API and the clientEmailitServerException- server-side failures returned by Emailit
Rate Limits
Rate limit metadata is exposed on:
EmailResponse.RateLimitInfoafter send operationsIEmailitClient.LastRateLimitInfoafter any request, includingTestConnectionAsync
var info = await client.TestConnectionAsync();
Console.WriteLine(client.LastRateLimitInfo?.DailyRemaining);
TestConnectionAsync throws the same typed exceptions as the rest of the client when the API is unreachable or rejects the request.
Packaging
Only the library project is packaged.
Both test projects are excluded from packaging because their .csproj files set:
<IsPackable>false</IsPackable>
Recommended packaging command:
dotnet pack src/Emailit.Client/Emailit.Client.csproj -c Release -o nupkg
This avoids packing the solution and guarantees that test projects do not enter the NuGet package.
Supported Endpoints
Emails
SendEmailAsyncGetEmailAsyncListEmailsAsyncUpdateScheduledEmailAsyncCancelEmailAsyncRetryEmailAsyncResendEmailAsync(legacy compatibility path)GetEmailMetaAsyncGetEmailBodyAsyncGetEmailRawAsyncGetEmailAttachmentsAsync
Domains
CreateDomainAsyncGetDomainAsyncListDomainsAsyncUpdateDomainAsyncVerifyDomainAsyncDeleteDomainAsync
API Keys
CreateApiKeyAsyncGetApiKeyAsyncListApiKeysAsyncUpdateApiKeyAsyncDeleteApiKeyAsync
Audiences and Subscribers
CreateAudienceAsyncGetAudienceAsyncListAudiencesAsyncUpdateAudienceAsyncDeleteAudienceAsyncAddSubscriberAsyncGetSubscriberAsyncListSubscribersAsyncUpdateSubscriberAsyncDeleteSubscriberAsync
Templates
CreateTemplateAsyncGetTemplateAsyncListTemplatesAsyncUpdateTemplateAsyncPublishTemplateAsyncDeleteTemplateAsync
Suppressions
CreateSuppressionAsyncGetSuppressionAsyncListSuppressionsAsyncUpdateSuppressionAsyncDeleteSuppressionAsync
Verification
VerifyEmailAsyncCreateVerificationListAsyncGetVerificationListAsyncListVerificationListsAsyncGetVerificationResultsAsyncExportVerificationResultsAsync
Contacts
CreateContactAsyncGetContactAsyncListContactsAsyncUpdateContactAsyncDeleteContactAsync
Events
ListEventsAsyncGetEventAsync
Webhooks
CreateWebhookAsyncGetWebhookAsyncListWebhooksAsyncUpdateWebhookAsyncDeleteWebhookAsync
Testing
The repository contains two test projects:
tests/Emailit.Client.Tests- unit tests with mocked HTTPtests/Emailit.Client.IntegrationTests- production integration tests
Required environment variables
$env:EMAILIT_INTEGRATION_API_KEY = "em_or_secret_key"
$env:EMAILIT_INTEGRATION_DOMAIN = "yourdomain.com"
$env:EMAILIT_INTEGRATION_TO_EMAIL = "you@example.com"
Optional:
$env:EMAILIT_INTEGRATION_BASE_URL = "https://api.emailit.com"
$env:EMAILIT_INTEGRATION_TIMEOUT_SECONDS = "60"
$env:EMAILIT_INTEGRATION_ENABLE_UNSTABLE = "true"
Run unit tests
dotnet test tests/Emailit.Client.Tests/Emailit.Client.Tests.csproj -c Release
Run stable production integration tests
dotnet test tests/Emailit.Client.IntegrationTests/Emailit.Client.IntegrationTests.csproj -c Release --filter "Stability!=Unstable"
Run unstable production integration tests too
dotnet test tests/Emailit.Client.IntegrationTests/Emailit.Client.IntegrationTests.csproj -c Release
Stable production integration coverage currently includes:
- connection check and rate limits
- domains and API keys
- audiences, subscribers, contacts, and suppressions
- templates
- emails and email sub-resources
- events
- single email verification
Unstable production coverage is kept separate for backend areas that are known to be inconsistent:
- webhooks
- verification list/results/export endpoints
- retry/resend behavior
When running the unstable suite, known backend inconsistencies are treated as observations rather than client regressions when:
- a retryable email candidate cannot be produced within the observation window
- a webhook create response returns an ID that never becomes readable
- verification list creation returns a production
500
Resources
License
MIT
| Product | Versions 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 is compatible. 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 is compatible. 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. |
-
net10.0
- Flurl (>= 4.0.0)
- Flurl.Http (>= 4.0.2)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
-
net8.0
- Flurl (>= 4.0.0)
- Flurl.Http (>= 4.0.2)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
-
net9.0
- Flurl (>= 4.0.0)
- Flurl.Http (>= 4.0.2)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v2.1.2 - Exception handling overhaul and production integration hardening
Error handling:
- richer exception context with request method, sanitized request URI, request ID, rate limits, transient flags, and optional diagnostic response capture
- ProblemDetails conversion for Emailit exceptions
- dedicated exception types for authorization, conflicts, transport failures, timeouts, deserialization failures, unexpected response shapes, server failures, and unprocessable requests
- TestConnectionAsync now throws typed exceptions instead of silently returning null
Quality:
- updated README to focus on consumer usage and operational guidance
- hardened unstable production integration scenarios around webhooks, retry/resend candidate selection, and verification list instability