Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ MAILINATOR_TEST_API_TOKEN=
# Optional values used by endpoint-specific integration tests.
MAILINATOR_TEST_DOMAIN_PRIVATE=
MAILINATOR_TEST_INBOX=
# Existing email in MAILINATOR_TEST_DOMAIN_PRIVATE, used by header and summary retrieval tests.
MAILINATOR_TEST_MESSAGE_ID=
MAILINATOR_TEST_PHONE_NUMBER=
MAILINATOR_TEST_MESSAGE_WITH_ATTACHMENT_ID=
MAILINATOR_TEST_ATTACHMENT_ID=
Expand All @@ -13,4 +15,6 @@ MAILINATOR_TEST_WEBHOOKTOKEN_CUSTOMSERVICE=
MAILINATOR_TEST_AUTH_SECRET=
MAILINATOR_TEST_AUTH_ID=
MAILINATOR_TEST_WEBHOOK_INBOX=
# Set to 1 to run domain webhook tests, which create four messages.
MAILINATOR_TEST_RUN_WEBHOOKS=0
MAILINATOR_TEST_WEBHOOK_CUSTOMSERVICE=
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,52 @@ All notable changes to this project will be documented in this file.

The format is based on *Keep a Changelog* and this project aims to follow *Semantic Versioning*.

## [2.0.0] - TBD

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set to today's date.


### Breaking changes

- Upgraded RestSharp from `112.0.0` to `114.0.0`. The SDK exposes RestSharp types through its public API, including `IHttpClient` and `DynamicJsonSerializer`, so this release requires a major version bump from `1.0.7`. Rebuild consuming applications and libraries, align direct RestSharp references with `114.0.0`, and review the [migration guidance](README.md#upgrading-from-107-to-200).

### Added

- Domain and inbox webhook injection via `PostWebhookMessageAsync` and `PostWebhookInboxMessageAsync`, supporting both webhook authentication forms, rich JSON payloads, offline unit tests, and opt-in integration tests.
- `MessagesClient.GetMessageTextAsync`, `GetMessageTextPlainAsync`, and `GetMessageTextHtmlAsync` with request/response models, offline route and JSON deserialization tests, and opt-in read-only integration tests.
- `MessagesClient.GetMessageSummaryAsync` with request/response models, offline request and deserialization tests, and an opt-in live test for an existing email.
- `MessagesClient.GetMessageHeadersAsync` with request/response models, offline request and deserialization tests, and an opt-in live test for an existing email.
- `MessagesClient.ListDomainMessagesAsync` and `ListDomainMessagesRequest` for domain-wide message listing, with optional inbox filtering and all documented listing parameters. Returns the existing `FetchInboxResponse` model.
- Offline tests for domain listing query parameters, default behavior, and wildcard filtering.
- Offline request-construction coverage for existing API clients, including routes, path parameters, query parameters, and request bodies.
- NuGet package lock files for the SDK, test projects, and OpenAPI coverage tool.

### Security

- Updated `System.Text.Json` to `10.0.12` via a direct package reference, retaining the remediation for CVE-2024-43485.

### Changed

- Updated `Newtonsoft.Json` to `13.0.4`.
- Migrated the development-only OpenAPI coverage tool to `Microsoft.OpenApi.YamlReader` / `Microsoft.OpenApi` `3.10.2`, replacing `Microsoft.OpenApi.Readers` and using asynchronous loading and the new parameter reference model. Coverage output is unchanged for the same Mailinator specification. These packages are not dependencies of the published SDK.
- Retained the coverage reader's transitive SharpYaml `2.1.5` dependency: testing `3.14.0` produces a `MissingMethodException` because its parser API is binary-incompatible with the current Microsoft reader.
- Updated both test projects to `Microsoft.NET.Test.Sdk` `18.10.1`, `MSTest.TestAdapter` `4.4.1`, and `MSTest.TestFramework` `4.4.1`.
- Migrated the live integration-test project to SDK-style `PackageReference` and the same current test stack.
- Updated integration-test exception assertions and binding redirects for MSTest 4 compatibility.
- Shared `.env` loading across integration tests while preserving existing environment-variable values.
- Refreshed NuGet lock files for the updated direct and transitive dependencies.
- Updated the README with an API reference link and migration guidance from `1.0.7` to `2.0.0`; clarified sort query values in the examples. SDK target frameworks remain .NET Framework 4.7.1 and .NET Standard 2.0.

### Removed

- Removed the manually maintained `REFERENCE.md`; use the README, examples, and Mailinator API reference instead.

### Deprecated

- `AuthenticatorsClient.GetAuthenticatorsAsync`, `GetAuthenticatorAsync`, and `GetAuthenticatorByIdAsync`; use `GetAuthenticatorsByIdAsync` for the documented stored-authenticator operation.

### Fixed

- Corrected sort query serialization in `ListDomainMessagesAsync` and `FetchInboxAsync`: `Sort.asc` sends `ascending` and `Sort.desc` sends `descending`, matching the OpenAPI contract. Public enum names remain unchanged. Added offline regression coverage for both sort directions on both methods.
- Forwarded the optional `delete` query parameter in `FetchInboxMessageAsync`.


## [1.0.7] - 2026-08-15

Expand Down
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
</Project>
130 changes: 127 additions & 3 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,13 @@ var request = new FetchInboxRequest
var response = await client.MessagesClient.FetchInboxAsync(request);
```

Use `Sort.asc` for ascending order or `Sort.desc` for descending order (the default). In version 2.0.0, both `FetchInboxAsync` and `ListDomainMessagesAsync` serialize these as the API query values `ascending` and `descending`.

For dependency migration steps, see [upgrading from 1.0.7 to 2.0.0](README.md#upgrading-from-107-to-200).

## Authenticators

Instant TOTP code + list authenticators:
Instant TOTP code + get a stored authenticator:

```csharp
using mailinator_csharp_client;
Expand All @@ -54,8 +58,6 @@ var client = new MailinatorClient("yourApiTokenHere");
var totp = await client.AuthenticatorsClient.InstantTOTP2FACodeAsync(
new InstantTOTP2FACodeRequest { TotpSecretKey = "yourAuthSecret" });

var authenticators = await client.AuthenticatorsClient.GetAuthenticatorsAsync();

var byId = await client.AuthenticatorsClient.GetAuthenticatorsByIdAsync(
new GetAuthenticatorsByIdRequest { Id = "yourAuthId" });
```
Expand All @@ -78,6 +80,73 @@ var domain = await client.DomainsClient.GetDomainAsync(

## Messages

### List domain messages

List messages across all inboxes in a domain:

```csharp
using mailinator_csharp_client.Models.Messages.Requests;

// Uses the authenticated client created in Setup.
var request = new ListDomainMessagesRequest
{
Domain = "your_private_domain.com",
Limit = 20
};

var response = await client.MessagesClient.ListDomainMessagesAsync(request);
var messages = response.Messages;

// Fetch the next page only when the API supplies a cursor.
if (!string.IsNullOrEmpty(response.Cursor))
{
request.Cursor = response.Cursor;
var nextPage = await client.MessagesClient.ListDomainMessagesAsync(request);
}
```

`Inbox` is an optional query filter: omit it or set it to `"*"` for all inboxes, or supply an inbox name/prefix such as `"orders*"`. The request also supports `Skip`, `Limit`, `Sort`, `DecodeSubject`, `Cursor`, `Full`, `Wait`, and `Delete`. Set `Full = true` to request full message content. `Delete` schedules deletion after retrieval (for example, `"30s"`); it is omitted by default.

The result is a `FetchInboxResponse`, sharing the inbox-listing response model and pagination cursor.

### Get message summary

```csharp
using mailinator_csharp_client.Models.Messages.Requests;

// Uses the authenticated client created in Setup.
var response = await client.MessagesClient.GetMessageSummaryAsync(
new GetMessageSummaryRequest
{
Domain = "your_private_domain.com",
MessageId = "your-message-id"
});

var summary = response.Summary;
```

`Summary` reuses the `Message` model and contains the subject, domain, sender (`From`), message ID, recipient (`To`), and timestamp (`Time`). This endpoint does not return body or attachment content.

### Get message headers

```csharp
using mailinator_csharp_client.Models.Messages.Requests;

// Uses the authenticated client created in Setup.
var response = await client.MessagesClient.GetMessageHeadersAsync(
new GetMessageHeadersRequest
{
Domain = "your_private_domain.com",
MessageId = "your-message-id"
});

var headers = response.Headers;
```

Use a message ID returned by inbox or domain listing. `Headers` is a `Dictionary<string, object>` that preserves custom header names. Values can be strings or JSON arrays (for example, `received`), matching the existing full-message header model.

### Post a message

Post (inject) a message:

```csharp
Expand Down Expand Up @@ -218,3 +287,58 @@ var customServiceInboxWebhook = await client.WebhooksClient.PrivateCustomService

- Ensure you’re using an API token from your Mailinator team settings.
- For webhook injection, use webhook tokens (`whtoken`) instead of your API token.

## Get message content

Use a message ID returned by inbox or domain listing:

```csharp
var extracted = await client.MessagesClient.GetMessageTextAsync(
new GetMessageTextRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" });
var plain = await client.MessagesClient.GetMessageTextPlainAsync(
new GetMessageTextPlainRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" });
var html = await client.MessagesClient.GetMessageTextHtmlAsync(
new GetMessageTextHtmlRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" });

string extractedText = extracted.Text;
string plainText = plain.TextPlain;
string htmlBody = html.TextHtml;
```

These endpoints return JSON wrappers with `text`, `text/plain`, and `text/html` fields respectively. The SDK preserves their content, including HTML markup and any quoted-printable artifacts such as `=C2=A0` in extracted text. Empty strings are preserved. These operations do not delete the message.

## Domain and inbox webhooks

These endpoints authenticate with webhook tokens; no API token is needed. Request types are in `mailinator_csharp_client.Models.Webhooks.Requests` and `WebhookMessage` is in `mailinator_csharp_client.Models.Webhooks.Entities`.

```csharp
var client = new MailinatorClient();
var webhookToken = Environment.GetEnvironmentVariable("MAILINATOR_WEBHOOK_TOKEN");
var payload = new WebhookMessage
{
To = "orders",
From = "sender@example.com",
Subject = "Order notification",
Text = "Order received",
Html = "<p>Order received</p>"
};

var domainResult = await client.WebhooksClient.PostWebhookMessageAsync(
new PostWebhookMessageRequest
{
Domain = "your-private-domain.com",
WebhookToken = webhookToken,
Webhook = payload
});

var inboxResult = await client.WebhooksClient.PostWebhookInboxMessageAsync(
new PostWebhookInboxMessageRequest
{
Domain = "your-private-domain.com",
Inbox = "orders",
WebhookToken = webhookToken,
Webhook = payload
});
```

Both methods also accept the webhook token in `Domain`; omit `WebhookToken` for that form. The payload's `To` field is required by the specification. `Headers` accepts a dictionary of string values, and `AdditionalProperties` accepts custom JSON fields. Both responses expose `Status` and `Id`. Existing private/custom-service webhook methods remain available.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mailinator C# SDK

The official C# SDK for the [Mailinator API](https://www.mailinator.com/documentation/docs/api/index.html). This package is a thin, asynchronous wrapper around the Mailinator REST API, and the [Mailinator OpenAPI specification](https://github.com/manybrain/mailinatordocs/blob/main/openapi/mailinator-api.yaml) is the source of truth for documented endpoints.
The official Mailinator C# SDK. This package is a thin, asynchronous wrapper around the Mailinator REST API, and the [Mailinator OpenAPI specification](https://github.com/manybrain/mailinatordocs/blob/main/openapi/mailinator-api.yaml) is the source of truth for documented endpoints.

The SDK targets .NET Framework 4.7.1 and .NET Standard 2.0.

Expand All @@ -24,6 +24,19 @@ PackageReference:
<PackageReference Include="MailinatorApiClient" Version="YOUR_VERSION" />
```

## Upgrading from 1.0.7 to 2.0.0

Version 2.0.0 upgrades RestSharp from 112.0.0 to 114.0.0. RestSharp types are part of this SDK's public API, including `IHttpClient` and `DynamicJsonSerializer`, so consumers must account for RestSharp's breaking changes:

- Align any direct RestSharp references with 114.0.0 and rebuild consuming applications and libraries.
- If you implement RestSharp's `IAuthenticator`, update `Authenticate` to accept the optional `CancellationToken` parameter.
- If you access `ReadOnlyRestClientOptions.FollowRedirects` or `MaxRedirects`, migrate to `RedirectOptions`.
- Recompile code using RestSharp's generic parameter extension methods, whose signatures now include an optional culture parameter.

See the [RestSharp 114 changelog](https://restsharp.dev/docs/changelog/) for details. The SDK continues to target .NET Framework 4.7.1 and .NET Standard 2.0.

Message listing now sends the documented `ascending` and `descending` sort query values. Continue using `Sort.asc` and `Sort.desc` in C#.

## Quick start

Create a Mailinator account, then obtain an API token from **Team Settings > API Tokens**. Keep the token outside your source code—for example, in an environment variable.
Expand Down Expand Up @@ -54,10 +67,11 @@ var response = await client.MessagesClient.FetchInboxAsync(

All API operations are asynchronous and end in `Async`. Operations are grouped under `MessagesClient`, `DomainsClient`, `AuthenticatorsClient`, `StatsClient`, `WebhooksClient`, and `RulesClient`.

## API reference
For complete workflows, including domain message listing, message content, headers, attachments, and webhooks, see [EXAMPLES.md](EXAMPLES.md).

## Documentation

- [Mailinator API reference](https://www.mailinator.com/documentation/docs/api/index.html) describes the REST API.
- [REFERENCE.md](REFERENCE.md) lists the operations currently exposed by this SDK.
- [EXAMPLES.md](EXAMPLES.md) contains examples for common SDK workflows.

## Authentication
Expand All @@ -78,7 +92,7 @@ See the [webhook examples](EXAMPLES.md#webhooks) for complete requests.

## Deprecated APIs

Some older SDK operations do not appear in the current OpenAPI specification. They remain available for compatibility but are marked with `[Obsolete]` and may be removed in a future major release. See the deprecation notes in [REFERENCE.md](REFERENCE.md#deprecated-operations) and the alignment work in [ROADMAP.md](ROADMAP.md).
Some older SDK operations do not appear in the current OpenAPI specification. They remain available for compatibility but are marked with `[Obsolete]` and may be removed in a future major release. See the compatibility decisions in [ROADMAP.md](ROADMAP.md#resolved-compatibility-decisions).

## Development

Expand Down
Loading