PostKit 10.3.0
dotnet add package PostKit --version 10.3.0
NuGet\Install-Package PostKit -Version 10.3.0
<PackageReference Include="PostKit" Version="10.3.0" />
<PackageVersion Include="PostKit" Version="10.3.0" />
<PackageReference Include="PostKit" />
paket add PostKit --version 10.3.0
#r "nuget: PostKit, 10.3.0"
#:package PostKit@10.3.0
#addin nuget:?package=PostKit&version=10.3.0
#tool nuget:?package=PostKit&version=10.3.0
PostKit
A MimeKit infused implementation of the Postmark API.
Release notes and upgrade guidance are maintained in CHANGES.md.
Quickstart
Prerequisites
- .NET 8.0 or later
- A Postmark account with a Server API Token
- Verified sender email addresses in your Postmark account
Installation
Install PostKit via NuGet:
dotnet add package PostKit
Current package targets net8.0, net9.0, and net10.0.
Configuration
Add PostKit to your services and configure your Postmark API token:
// Program.cs (Minimal API / Web App)
using PostKit;
var builder = WebApplication.CreateBuilder(args);
// Add PostKit to services
builder.Services.AddPostKit(builder.Configuration);
var app = builder.Build();
In ASP.NET Core apps, builder.Services.AddPostKit() also works because IConfiguration is already registered in the service provider. The explicit IConfiguration overload shown above validates that the PostKit section exists
immediately and does not require registering IConfiguration yourself.
Configure your Postmark Server API Token in appsettings.json:
{
"PostKit": {
"ServerApiToken": "your-postmark-server-token-here",
"AccountApiToken": "your-postmark-account-token-here"
}
}
AccountApiToken is optional unless you call account-level endpoints such as template push or data removal.
Or set it via environment variables:
PostKit__ServerApiToken=your-postmark-server-token-here
If you need multiple Postmark configurations (for example, separate servers or tenants), register keyed services with AddKeyedPostKit. Each keyed registration binds to PostKit:{configurationKey}; if you omit configurationKey, the
serviceKey.ToString() value is used.
When IConfiguration is available from dependency injection, you can use the shorter overloads:
builder.Services.AddKeyedPostKit("Marketing"); // binds PostKit:Marketing
builder.Services.AddKeyedPostKit("Production", "Default"); // resolves with key "Production", binds PostKit:Default
When you are configuring a standalone ServiceCollection or want missing sections to fail immediately, pass the configuration explicitly:
builder.Services.AddKeyedPostKit("Marketing", builder.Configuration); // binds PostKit:Marketing
public enum PostmarkServer
{
Development,
Production
}
builder.Services.AddKeyedPostKit(PostmarkServer.Development, builder.Configuration); // binds PostKit:Development
builder.Services.AddKeyedPostKit(PostmarkServer.Production, builder.Configuration, "Default"); // binds PostKit:Default
{
"PostKit": {
"Marketing": { "ServerApiToken": "token-1" },
"Development": { "ServerApiToken": "token-2" },
"Default": { "ServerApiToken": "token-3" }
}
}
HTTP Resilience And Retry Safety
Postmark does not currently support idempotency keys. Repeating an unsafe request such as POST /email can therefore send the same email more than once. Custom Message-ID headers and metadata are useful for correlation, but Postmark does not document
them as deduplication keys.
PostKit proactively paces all requests against known or observed Postmark rate limits. When Postmark returns 429 Too Many Requests, PostKit honors Retry-After when present and otherwise uses bounded exponential backoff with jitter, but it retries only
safe methods such as GET. Unsafe methods (POST, PUT, PATCH, and DELETE) return the first response without an automatic retry.
An HttpClient resilience handler runs inside PostKit's request and can retry before PostKit observes the result. Because the .NET standard resilience handler retries unsafe methods by default, PostKit's named HTTP client removes resilience handlers
registered earlier and installs a PostKit-owned standard pipeline. The default pipeline:
- retains standard rate limiting, total and attempt timeouts, safe-method retries, and circuit breaking;
- disables retries for unsafe methods; and
- excludes
429 Too Many Requestsfrom transport retry and circuit breaking so PostKit's shared rate-limit buckets remain the sole owner of429backoff.
Register application-wide HTTP client defaults before calling AddPostKit. No additional configuration is required for the safe default. To customize the named client, call ConfigurePostKitHttpClient after every AddPostKit and AddKeyedPostKit
registration:
using PostKit;
builder.Services.AddPostKit(builder.Configuration);
builder.Services.ConfigurePostKitHttpClient(httpClient =>
{
httpClient.SetHandlerLifetime(TimeSpan.FromMinutes(10));
});
Configurations added through this hook extend the existing named client. Adding another resilience handler without first removing PostKit's handler creates nested pipelines. A caller that replaces the default pipeline or re-enables unsafe retries assumes
responsibility for duplicate effects and for ensuring that PostKit remains the sole owner of 429 retries.
The application remains responsible for business-level idempotency. For security emails, billing notifications, and other sensitive operations, use a stable application operation ID and durable or process-local delivery state appropriate to the workflow.
Do not blindly retry an unsafe request that returns PostmarkUnknownOutcomeError or a successful unsafe request that returns PostmarkInvalidResponseError.
PostKit validates that the selected configuration section defines ServerApiToken, AccountApiToken, or both. When you use the explicit configuration overloads above, missing sections fail immediately and missing tokens fail during
startup or first resolution. Server-level endpoints still require ServerApiToken; account-level endpoints still require AccountApiToken.
Resolve keyed clients with the standard keyed DI APIs:
var marketingClient = app.Services.GetRequiredKeyedService<IPostKitClient>("Marketing");
Basic Usage
Most application code will use these namespaces:
using PostKit;
using PostKit.Common;
using PostKit.Emails;
Add the endpoint namespace you need when working outside basic email sending: PostKit.BulkEmails, PostKit.Bounces, PostKit.DataRemovals, PostKit.Domains, PostKit.InboundRules, PostKit.MessageStreams, PostKit.Messages,
PostKit.SenderSignatures, PostKit.Servers, PostKit.Stats, PostKit.Suppressions, PostKit.Templates, or PostKit.Webhooks.
PostKit uses a fluent builder pattern with the following capabilities:
- Email Addresses: Support for simple strings, address/display-name pairs, or MimeKit
MailboxAddressobjects - Display Names: Address/display-name overloads are address-first, for example
.From("sender@example.com", "Sender Name") - Multiple Recipients: Repeat
To(),Cc(),Bcc(), orReplyTo()to add additional recipients - Templates: Send templated emails by template ID or alias, including in batches
- Bulk Email API: Submit broadcast bulk email jobs and poll their processing status
- Suppressions API: Query, create, and delete message stream suppressions
- Validation: Builder validation covers required fields, recipient counts, headers, metadata, message streams, template/body exclusivity, and size limits. Postmark still performs final sender and recipient validation
Simple Email
using PostKit;
using PostKit.Emails;
// Inject IPostKitClient via dependency injection
public class EmailService
{
private readonly IPostKitClient _postKitClient;
public EmailService(IPostKitClient postKitClient)
{
_postKitClient = postKitClient;
}
public async Task SendWelcomeEmailAsync()
{
var email = Email.Compose()
.From("noreply@yourapp.com")
.To("user@example.com")
.Subject("Welcome to Our Service!")
.TextBody("Thank you for signing up!")
.Build();
await _postKitClient.SendEmailAsync(email);
}
}
Rich HTML Email
var email = Email.Compose()
.From("sarah@company.com", "Sarah Johnson")
.To("customer@example.com")
.Subject("Your Order Confirmation")
.HtmlBody(@"
<h1>Order Confirmed!</h1>
<p>Thank you for your purchase. Your order #12345 has been confirmed.</p>
<a href='https://yourapp.com/orders/12345'>View Order Details</a>
")
.TextBody("Order Confirmed! Thank you for your purchase. Your order #12345 has been confirmed. View details at: https://yourapp.com/orders/12345")
.Build();
await _postKitClient.SendEmailAsync(email);
Multiple Recipients
var email = Email.Compose()
.From("notifications@company.com")
.To(new[] { "user1@example.com", "user2@example.com" })
.Cc("manager@company.com")
.Bcc("admin@company.com")
.Subject("Team Update")
.TextBody("Important team announcement...")
.Build();
await _postKitClient.SendEmailAsync(email);
Batch Sending
var welcomeEmail = Email.Compose()
.From("noreply@yourapp.com")
.To("user1@example.com")
.Subject("Welcome!")
.TextBody("Thanks for signing up")
.Build();
var reminderEmail = Email.Compose()
.From("noreply@yourapp.com")
.To("user2@example.com")
.Subject("Complete Your Profile")
.TextBody("Finish setting up your account")
.Build();
var batchResult = await _postKitClient.SendEmailBatchAsync(new[] { welcomeEmail, reminderEmail });
if (batchResult.IsSuccess(out var batchResponse))
{
if (!batchResponse.IsSuccessful)
{
foreach (var result in batchResponse.Results)
{
if (result.IsFailure(out var error, out _))
Console.WriteLine($"Batch item failed: {error.Message}");
}
}
}
Templates
var email = Email.FromTemplate("welcome-email", inlineCss: true)
.From("noreply@yourapp.com")
.To("user@example.com")
.WithModel(new
{
name = "Alice",
product = "PostKit"
})
.Build();
await _postKitClient.SendEmailAsync(email);
When batching template emails, every email in the batch must use a template. Mixing templated and non-templated emails in the same batch is rejected by the client.
Template models use System.Text.Json web defaults by default, so CLR properties such as FirstName serialize as firstName. If you want different naming,
set PostKitTemplateModelSerialization.DefaultSerializerOptions globally or pass explicit serializer options to WithModel(...) for that call.
Advanced Features
var email = Email.Compose()
.From("newsletter@company.com")
.To("subscriber@example.com")
.ReplyTo("support@company.com")
.Subject("Monthly Newsletter")
.HtmlBody("<h1>Newsletter</h1><p>Check out our latest updates!</p>")
.WithTag("newsletter")
.AddMetadata("campaign", "monthly-2024")
.AddMetadata("segment", "premium-users")
.EnableOpenTracking()
.UseLinkTracking(LinkTracking.HtmlAndText)
.UseMessageStream(MessageStream.Broadcast)
.AddHeader("X-Campaign-ID", "CAMP-001")
.Build();
await _postKitClient.SendEmailAsync(email);
Attachments and Inline Images
var invoice = Attachment.Create(
name: "invoice.pdf",
contentType: "application/pdf",
content: await File.ReadAllBytesAsync("invoice.pdf"));
var logo = Attachment.Create(
name: "logo.png",
contentType: "image/png",
content: await File.ReadAllBytesAsync("logo.png"),
contentId: "logo@yourapp.com");
var email = Email.Compose()
.From("billing@company.com")
.To("customer@example.com")
.Subject("Your Monthly Invoice")
.HtmlBody($"<p>Please find your invoice attached.</p><img src=\"{logo.ContentId}\" alt=\"Company Logo\" />")
.AddAttachment(invoice)
.AddAttachment(logo)
.Build();
await _postKitClient.SendEmailAsync(email);
Bulk Emails
var bulkEmail = BulkEmail.FromTemplate("subscriber-welcome")
.From("newsletter@company.com")
.UseMessageStream(MessageStream.Broadcast)
.AddMessage(BulkEmailMessage.FromTemplate()
.To("alice@example.com")
.WithModel(new { firstName = "Alice" })
.Build())
.AddMessage(BulkEmailMessage.FromTemplate()
.To("bob@example.com")
.WithModel(new { firstName = "Bob" })
.Build())
.Build();
var submitResult = await _postKitClient.SendBulkEmailAsync(bulkEmail);
if (submitResult.IsSuccess(out var submitted))
{
var statusResult = await _postKitClient.GetBulkEmailStatusAsync(submitted.Id);
}
PostKit enforces the Bulk Email API's broadcast-stream requirement. MessageStream.Transactional and the outbound stream ID are rejected by the bulk builder.
Use BulkEmail.Compose() and BulkEmailMessage.Compose() for non-template bulk jobs with a shared subject and body:
var bulkEmail = BulkEmail.Compose()
.From("announcements@company.com")
.Subject("Service update")
.TextBody("A service update is available.")
.UseMessageStream(MessageStream.Broadcast)
.AddMessage(BulkEmailMessage.Compose()
.To("alice@example.com")
.Build())
.AddMessage(BulkEmailMessage.Compose()
.To("bob@example.com")
.Build())
.Build();
If Postmark accepts the bulk request but reports unprocessable messages, SendBulkEmailAsync returns a failed result with BulkEmailValidationError. The error includes both the accepted bulk job and the server-provided
unprocessable-content payload.
using PostKit.Errors;
var result = await _postKitClient.SendBulkEmailAsync(bulkEmail);
if (result.IsFailure(out var error, out _) && error is BulkEmailValidationError validationError)
{
Console.WriteLine($"Bulk request accepted as {validationError.AcceptedJob.Id}");
Console.WriteLine(validationError.UnprocessableContent);
}
Bounces
using MimeKit;
using PostKit.Bounces;
using PostKit.Common;
var pageResult = await _postKitClient.GetBouncesAsync(
MessageStream.Transactional,
count: 100,
query: new BounceQuery
{
Type = BounceType.HardBounce,
Inactive = true,
EmailFilter = new MailboxAddress(null, "user@example.com"),
});
if (pageResult.IsSuccess(out var page))
{
foreach (var bounce in page.Bounces)
Console.WriteLine($"{bounce.Id}: {bounce.Type} for {bounce.Email}");
}
The bounce client also supports GetBounceAsync, GetBounceDumpAsync, GetDeliveryStatsAsync, and ActivateBounceAsync. Bounce date filters accept DateTimeOffset values and are converted to Postmark's US Eastern time before the
request is made.
Messages
using MimeKit;
using PostKit.Common;
using PostKit.Messages;
var messagesResult = await _postKitClient.SearchOutboundMessagesAsync(
MessageStream.Transactional,
count: 100,
query: new OutboundMessageQuery
{
Recipient = new MailboxAddress(null, "user@example.com"),
Status = OutboundMessageStatus.Sent,
Metadata = new OutboundMessageMetadataFilter { Name = "campaign", Value = "welcome" },
});
if (messagesResult.IsSuccess(out var page))
{
foreach (var message in page.Messages)
Console.WriteLine($"{message.MessageId}: {message.Status} {message.Subject}");
var firstMessage = page.Messages.FirstOrDefault();
if (firstMessage is not null)
{
var detailsResult = await _postKitClient.GetOutboundMessageDetailsAsync(firstMessage.MessageId);
var dumpResult = await _postKitClient.GetOutboundMessageDumpAsync(firstMessage.MessageId);
}
}
Outbound and inbound message date filters accept DateTimeOffset values and are converted to Postmark's US Eastern time before the request is made. Postmark currently supports one metadata filter per outbound message search.
Outbound message details expose nullable TextBody, HtmlBody, and Body properties because Postmark can omit content variants or the raw source on the details response. Use GetOutboundMessageDumpAsync when you specifically need the
raw source. The Messages API also includes SearchInboundMessagesAsync, GetInboundMessageDetailsAsync, BypassInboundMessageRulesAsync, RetryInboundMessageAsync, SearchMessageOpensAsync, GetMessageOpensAsync,
SearchMessageClicksAsync, and GetMessageClicksAsync.
Templates, Webhooks, Stats, And Account APIs
using PostKit.DataRemovals;
using PostKit.Domains;
using PostKit.InboundRules;
using PostKit.MessageStreams;
using PostKit.SenderSignatures;
using PostKit.Servers;
using PostKit.Stats;
using PostKit.Templates;
using PostKit.Webhooks;
var template = await _postKitClient.CreateTemplateAsync(new TemplateCreateParameters
{
Name = "Welcome",
Alias = "welcome-v1",
Subject = "Welcome",
HtmlBody = "<h1>Hello {{name}}</h1>",
});
var webhook = await _postKitClient.CreateWebhookAsync(new WebhookCreateParameters
{
Url = "https://example.com/postmark/webhook",
MessageStreamId = "outbound",
Triggers = new WebhookTriggers { Bounce = new WebhookContentTrigger { Enabled = true, IncludeContent = false } },
});
var stats = await _postKitClient.GetOutboundStatsOverviewAsync(new OutboundStatsQuery
{
FromDate = new DateOnly(2026, 1, 1),
ToDate = new DateOnly(2026, 1, 31),
MessageStreamId = "outbound",
});
var inboundRule = await _postKitClient.CreateInboundRuleTriggerAsync("blocked-sender@example.com");
var dataRemoval = await _postKitClient.CreateDataRemovalAsync(new DataRemovalCreateParameters
{
RequestedBy = "privacy@example.com",
RequestedFor = "recipient@example.com",
NotifyWhenCompleted = true,
});
var servers = await _postKitClient.ListServersAsync(query: new ServerQuery { Name = "Marketing" });
var streams = await _postKitClient.ListMessageStreamsAsync(new MessageStreamQuery { IncludeArchivedStreams = true });
var domains = await _postKitClient.ListDomainsAsync();
var signatures = await _postKitClient.ListSenderSignaturesAsync();
Template CRUD, webhooks, inbound rules, stats, and message streams use server API tokens. PushTemplatesAsync, server management by ID, domains, sender signatures, and data-removal calls use Postmark account-level endpoints and require
PostKit:AccountApiToken.
Suppressions
using PostKit.Common;
using PostKit.Suppressions;
var suppressionsResult = await _postKitClient.GetSuppressionsAsync(
MessageStream.Broadcast,
new SuppressionQuery
{
Reason = SuppressionReason.ManualSuppression,
FromDate = new DateOnly(2026, 3, 1),
ToDate = new DateOnly(2026, 3, 31),
});
if (suppressionsResult.IsSuccess(out var dump))
{
foreach (var suppression in dump.Suppressions)
Console.WriteLine($"{suppression.EmailAddress}: {suppression.Reason}");
}
var created = await _postKitClient.CreateSuppressionsAsync(MessageStream.Broadcast, "user@example.com");
var deleted = await _postKitClient.DeleteSuppressionsAsync(MessageStream.Broadcast, "user@example.com");
GetSuppressionsAsync maps the stream-scoped Postmark suppression dump, including HardBounce, SpamComplaint, and ManualSuppression reasons. Create and delete calls return per-address statuses from Postmark; deleting a
HardBounce suppression reactivates that address, while Postmark does not allow deleting SpamComplaint suppressions.
Template Model Serialization
By default, template models use System.Text.Json web defaults with null values omitted. That means CLR property names are camel-cased.
using System.Text.Json;
using System.Text.Json.Serialization;
using PostKit;
using PostKit.Emails;
PostKitTemplateModelSerialization.DefaultSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = null,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
var serializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = null,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
var email = Email.FromTemplate("welcome-email")
.From("noreply@yourapp.com")
.To("user@example.com")
.WithModel(new
{
FirstName = "Alice"
}, serializerOptions)
.Build();
Size Limits
Postmark limits TextBody and HtmlBody to 5 MB each, email message size to 10 MB, bulk email payloads to 50 MB, and email batches to 500 items / 50 MB. PostKit uses conservative lower-bound estimates based on already-materialized values
to
prevent grossly oversized requests without doing expensive serialization. The local pre-checks count template-model bytes, body bytes, Base64 attachment bytes, and custom-header bytes, and the batch/bulk payload checks also include metadata
bytes; Postmark remains the authoritative source of truth for the final server-side size checks.
Error Handling
PostKit uses LightResults for error handling. Every IPostKitClient operation returns a Result<T> value. Add using PostKit.Errors; when you want to inspect concrete error types.
SendEmailAsync returns a Result<EmailSubmission> that contains either the response or error information:
var result = await _postKitClient.SendEmailAsync(email);
if (result.IsSuccess(out var response, out var error))
{
// Email sent successfully
Console.WriteLine($"Email sent with MessageId: {response.MessageId}");
Console.WriteLine($"Internet Message-Id: {response.InternetMessageId}");
}
else
{
// Handle the error
Console.WriteLine($"Failed to send email: {error.Message}");
}
Error Types
PostKit may return different types of errors depending on the failure scenario:
PostmarkUnknownOutcomeError - Returned when PostKit does not receive a conclusive HTTP response because of a transport failure or timeout. IsRetrySafe identifies safe methods such as GET. For unsafe methods, Postmark may have processed the operation,
so the application must not retry unless it can prevent duplicate effects:
if (error is PostmarkUnknownOutcomeError unknownOutcomeError)
{
Console.WriteLine($"Unknown outcome: {unknownOutcomeError.Method} {unknownOutcomeError.Endpoint}");
if (!unknownOutcomeError.IsRetrySafe)
{
// Reconcile the operation through application delivery state before retrying.
}
}
PostmarkInvalidResponseError - Returned when Postmark supplies a response that cannot be deserialized or does not match the documented response contract. Examples include malformed JSON, missing acceptance fields, invalid message identifiers, and mismatched batch result counts. Repeated occurrences most likely indicate a change in the Postmark API, and the error message directs the developer to open an issue in the PostKit repository:
if (error is PostmarkInvalidResponseError invalidResponseError)
{
Console.WriteLine($"Invalid response: {invalidResponseError.Method} {invalidResponseError.Endpoint}");
Console.WriteLine($"HTTP status: {invalidResponseError.StatusCode}");
}
When an invalid response follows a successful unsafe request, the operation may already have been processed and must not be blindly retried.
PostmarkError - Returned when Postmark supplies a valid, structured Postmark error payload. This includes Postmark API validation errors and per-email failures inside a successfully processed batch request:
if (error is PostmarkError postmarkError)
{
Console.WriteLine($"Postmark error: {postmarkError.ErrorCode} - {postmarkError.Message}");
// Handle specific error codes
switch (postmarkError.ErrorCode)
{
case PostmarkErrorCode.SenderSignatureNotFound:
// The From address doesn't have a verified sender signature
break;
case PostmarkErrorCode.InvalidEmailRequest:
// The email request validation failed
break;
case PostmarkErrorCode.NotAllowedToSend:
// Account has run out of credits
break;
}
}
HttpError - Returned when Postmark or an intermediary supplies a non-success HTTP response without a recognized structured Postmark error. RetryAfter contains the requested delay when the response provides a valid Retry-After header:
if (error is HttpError httpError)
{
Console.WriteLine($"HTTP error: {httpError.StatusCode} - {httpError.Message}");
Console.WriteLine($"Retry after: {httpError.RetryAfter}");
}
Caller-requested cancellation continues to throw OperationCanceledException. If cancellation happens after an unsafe request was dispatched, its outcome can also be unknown, so cancellation should not trigger an unconditional retry.
BulkEmailValidationError - Returned when the Bulk Email API accepts a request but reports unprocessable messages:
if (error is BulkEmailValidationError bulkError)
{
Console.WriteLine($"Accepted bulk job: {bulkError.AcceptedJob.Id}");
Console.WriteLine(bulkError.UnprocessableContent);
}
See PostmarkErrorCode enum for the complete list of possible Postmark error codes.
Message Streams
Postmark supports different message streams for different types of emails. PostKit maps MessageStream.Transactional to outbound and MessageStream.Broadcast to broadcast. The Bulk Email API only supports broadcast streams.
// For transactional emails (default)
.UseMessageStream(MessageStream.Transactional)
// For broadcast/marketing emails
.UseMessageStream(MessageStream.Broadcast)
// Or use a custom stream ID
.UseMessageStream("custom-stream-id")
Complete Console Application Example
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using PostKit;
using PostKit.Emails;
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["PostKit:ServerApiToken"] = "your-postmark-server-token-here",
})
.Build();
var services = new ServiceCollection();
services.AddLogging();
services.AddPostKit(configuration);
using var serviceProvider = services.BuildServiceProvider();
// Get the PostKit client
var postKitClient = serviceProvider.GetRequiredService<IPostKitClient>();
// Create and send an email
var email = Email.Compose()
.From("test@yourapp.com")
.To("recipient@example.com")
.Subject("Test Email from PostKit")
.TextBody("Hello from PostKit! This email was sent using the PostKit library.")
.HtmlBody("<h1>Hello from PostKit!</h1><p>This email was sent using the <strong>PostKit</strong> library.</p>")
.Build();
await postKitClient.SendEmailAsync(email);
Console.WriteLine("Email sent successfully!");
Development
The following tables track development progress and map the different Postmark API endpoints to their respective methods in PostKit.
Email API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Send a single email | IPostKitClient.SendEmailAsync |
| ✅ | Send batch emails | IPostKitClient.SendEmailBatchAsync |
Bulk Email API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Send bulk emails BETA | IPostKitClient.SendBulkEmailAsync |
| ✅ | Get the status/details of a bulk API request BETA | IPostKitClient.GetBulkEmailStatusAsync |
Bounce API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Get delivery stats | IPostKitClient.GetDeliveryStatsAsync |
| ✅ | Get bounces | IPostKitClient.GetBouncesAsync |
| ✅ | Get a single bounce | IPostKitClient.GetBounceAsync |
| ✅ | Get bounce dump | IPostKitClient.GetBounceDumpAsync |
| ✅ | Activate a bounce | IPostKitClient.ActivateBounceAsync |
| ✅ | Bounce types |
Templates API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Send email with template | IPostKitClient.SendEmailAsync |
| ✅ | Send batch with templates | IPostKitClient.SendEmailBatchAsync |
| ✅ | Push templates to another server | IPostKitClient.PushTemplatesAsync |
| ✅ | Get a template | IPostKitClient.GetTemplateAsync |
| ✅ | Create a template | IPostKitClient.CreateTemplateAsync |
| ✅ | Edit a template | IPostKitClient.EditTemplateAsync |
| ✅ | List templates | IPostKitClient.ListTemplatesAsync |
| ✅ | Delete a template | IPostKitClient.DeleteTemplateAsync |
| ✅ | Validate a template | IPostKitClient.ValidateTemplateAsync |
Server API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Get the server | IPostKitClient.GetServerAsync |
| ✅ | Edit the server | IPostKitClient.EditServerAsync |
Servers API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Get a server | IPostKitClient.GetServerAsync |
| ✅ | Create a server | IPostKitClient.CreateServerAsync |
| ✅ | Edit a server | IPostKitClient.EditServerAsync |
| ✅ | List servers | IPostKitClient.ListServersAsync |
| ✅ | Delete a server | IPostKitClient.DeleteServerAsync |
Message Streams API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | List message streams | IPostKitClient.ListMessageStreamsAsync |
| ✅ | Get a message stream | IPostKitClient.GetMessageStreamAsync |
| ✅ | Edit a message stream | IPostKitClient.EditMessageStreamAsync |
| ✅ | Create a message stream | IPostKitClient.CreateMessageStreamAsync |
| ✅ | Archive a message stream | IPostKitClient.ArchiveMessageStreamAsync |
| ✅ | Unarchive a message stream | IPostKitClient.UnarchiveMessageStreamAsync |
Messages API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Outbound message search | IPostKitClient.SearchOutboundMessagesAsync |
| ✅ | Outbound message details | IPostKitClient.GetOutboundMessageDetailsAsync |
| ✅ | Outbound message dump | IPostKitClient.GetOutboundMessageDumpAsync |
| ✅ | Inbound message search | IPostKitClient.SearchInboundMessagesAsync |
| ✅ | Inbound message details | IPostKitClient.GetInboundMessageDetailsAsync |
| ✅ | Bypass rules for a blocked inbound message | IPostKitClient.BypassInboundMessageRulesAsync |
| ✅ | Retry a failed inbound message for processing | IPostKitClient.RetryInboundMessageAsync |
| ✅ | Message opens | IPostKitClient.SearchMessageOpensAsync |
| ✅ | Opens for a single message | IPostKitClient.GetMessageOpensAsync |
| ✅ | Message clicks | IPostKitClient.SearchMessageClicksAsync |
| ✅ | Clicks for a single message | IPostKitClient.GetMessageClicksAsync |
Domains API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | List domains | IPostKitClient.ListDomainsAsync |
| ✅ | Get domain details | IPostKitClient.GetDomainAsync |
| ✅ | Create domain | IPostKitClient.CreateDomainAsync |
| ✅ | Edit domain | IPostKitClient.EditDomainAsync |
| ✅ | Delete domain | IPostKitClient.DeleteDomainAsync |
| ✅ | Verify DKIM | IPostKitClient.VerifyDomainDkimAsync |
| ✅ | Verify Return-Path | IPostKitClient.VerifyDomainReturnPathAsync |
| ✅ | Verify an SPF record | IPostKitClient.VerifyDomainSpfAsync |
| ✅ | Rotate DKIM keys | IPostKitClient.RotateDomainDkimAsync |
Sender signatures API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | List sender signatures | IPostKitClient.ListSenderSignaturesAsync |
| ✅ | Get sender signature | IPostKitClient.GetSenderSignatureAsync |
| ✅ | Create a signature | IPostKitClient.CreateSenderSignatureAsync |
| ✅ | Edit a signature | IPostKitClient.EditSenderSignatureAsync |
| ✅ | Delete a signature | IPostKitClient.DeleteSenderSignatureAsync |
| ✅ | Resend a confirmation | IPostKitClient.ResendSenderSignatureConfirmationAsync |
| ✅ | Verify an SPF record | IPostKitClient.VerifySenderSignatureSpfAsync |
| ✅ | Request a new DKIM | IPostKitClient.RequestNewDkimForSenderSignatureAsync |
Stats API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Get outbound overview | IPostKitClient.GetOutboundStatsOverviewAsync |
| ✅ | Get sent counts | IPostKitClient.GetOutboundSentStatsAsync |
| ✅ | Get bounce counts | IPostKitClient.GetOutboundBounceStatsAsync |
| ✅ | Get spam complaints | IPostKitClient.GetOutboundSpamComplaintStatsAsync |
| ✅ | Get tracked email counts | IPostKitClient.GetOutboundTrackedEmailStatsAsync |
| ✅ | Get email open counts | IPostKitClient.GetOutboundOpenStatsAsync |
| ✅ | Get email platform usage | IPostKitClient.GetOutboundEmailPlatformStatsAsync |
| ✅ | Get email client usage | IPostKitClient.GetOutboundEmailClientStatsAsync |
| ✅ | Get click counts | IPostKitClient.GetOutboundClickStatsAsync |
| ✅ | Get browser usage | IPostKitClient.GetOutboundClickBrowserStatsAsync |
| ✅ | Get browser platform usage | IPostKitClient.GetOutboundClickPlatformStatsAsync |
| ✅ | Get click location | IPostKitClient.GetOutboundClickLocationStatsAsync |
Triggers: Inbound rules
| Endpoint | Implementation | |
|---|---|---|
| ✅ | List inbound rule triggers | IPostKitClient.ListInboundRuleTriggersAsync |
| ✅ | Create an inbound rule trigger | IPostKitClient.CreateInboundRuleTriggerAsync |
| ✅ | Delete a single trigger | IPostKitClient.DeleteInboundRuleTriggerAsync |
Webhooks API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | List webhooks | IPostKitClient.ListWebhooksAsync |
| ✅ | Get a webhook | IPostKitClient.GetWebhookAsync |
| ✅ | Create a webhook | IPostKitClient.CreateWebhookAsync |
| ✅ | Edit a webhook | IPostKitClient.EditWebhookAsync |
| ✅ | Delete a webhook | IPostKitClient.DeleteWebhookAsync |
Suppressions API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Suppression dump | IPostKitClient.GetSuppressionsAsync |
| ✅ | Create a Suppression | IPostKitClient.CreateSuppressionsAsync |
| ✅ | Delete a Suppression | IPostKitClient.DeleteSuppressionsAsync |
Data Removal API
| Endpoint | Implementation | |
|---|---|---|
| ✅ | Create a Data Removal request | IPostKitClient.CreateDataRemovalAsync |
| ✅ | Check a Data Removal request status | IPostKitClient.GetDataRemovalAsync |
| 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
- JetBrains.Annotations (>= 2026.2.0)
- LightResults (>= 10.0.4)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Http.Resilience (>= 10.8.0)
- Microsoft.Extensions.Telemetry.Abstractions (>= 10.8.0)
- MimeKit (>= 4.17.0)
-
net8.0
- JetBrains.Annotations (>= 2026.2.0)
- LightResults (>= 10.0.4)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Http.Resilience (>= 10.8.0)
- Microsoft.Extensions.Telemetry.Abstractions (>= 10.8.0)
- MimeKit (>= 4.17.0)
-
net9.0
- JetBrains.Annotations (>= 2026.2.0)
- LightResults (>= 10.0.4)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Http.Resilience (>= 10.8.0)
- Microsoft.Extensions.Telemetry.Abstractions (>= 10.8.0)
- MimeKit (>= 4.17.0)
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 |
|---|---|---|
| 10.3.0 | 49 | 8/9/2026 |
| 10.2.1 | 373 | 7/23/2026 |
| 10.2.0 | 688 | 6/19/2026 |
| 10.1.7 | 206 | 6/11/2026 |
| 10.1.6 | 129 | 6/11/2026 |
| 10.1.5 | 121 | 6/11/2026 |
| 10.1.4 | 136 | 6/11/2026 |
| 10.1.3 | 140 | 6/11/2026 |
| 10.1.2 | 149 | 6/9/2026 |
| 10.1.1 | 182 | 5/22/2026 |
| 10.1.0 | 1,560 | 4/29/2026 |
| 10.0.3 | 1,768 | 3/2/2026 |
| 10.0.2 | 662 | 3/2/2026 |
| 10.0.1 | 14,285 | 12/22/2025 |
| 10.0.0 | 14,215 | 12/16/2025 |
| 10.0.0-preview.1 | 596 | 11/15/2025 |
| 9.0.0-preview.7 | 414 | 10/13/2025 |
| 9.0.0-preview.6 | 189 | 10/13/2025 |
| 9.0.0-preview.5 | 681 | 10/8/2025 |
| 9.0.0-preview.4 | 205 | 9/24/2025 |
