Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1ce3733
Created BulkEmailRequest model and validators. Refactored existing em…
zhaparoff Apr 2, 2025
55b2dcd
Renamed abstractions to align with API naming.
zhaparoff Apr 2, 2025
e580b58
Updates NuGet
zhaparoff Apr 2, 2025
07bd2ca
Add response, cleanup tests.
zhaparoff Apr 2, 2025
9607b81
Batch WiP
zhaparoff Apr 6, 2025
8c2c6d1
Merge latest main
zhaparoff Apr 16, 2025
dc773a0
Batch emails implementation + tests
zhaparoff Apr 16, 2025
56ab5e7
Adds integration tests for batch email
zhaparoff May 7, 2025
59fc39f
Merge branch 'main' into feature/bulk-email
dr-3lo Sep 23, 2025
5b0b289
Merge branch 'main' into feature/bulk-email
dr-3lo Sep 26, 2025
22b16ba
Merge branch 'main' into feature/bulk-email
dr-3lo Sep 29, 2025
0cd488b
post merge fix
dr-3lo Sep 29, 2025
e6a91af
Feature - Batch Email Send (#95)
dr-3lo Oct 8, 2025
82059b6
Merge branch 'main' into feature/95-batch-email
dr-3lo Oct 8, 2025
df86eae
Merge branch 'feature/95-batch-email' of https://github.com/railsware…
dr-3lo Oct 8, 2025
374bd14
post merge fix
dr-3lo Oct 8, 2025
47ce28a
Feature - Batch Email Send (#95)
dr-3lo Oct 9, 2025
31347a0
Feature - Batch Email Send (#95)
dr-3lo Oct 9, 2025
7d85d61
Feature - Batch Email Send (#95)
dr-3lo Oct 9, 2025
5978b8e
Feature - Batch Email Send (#95)
dr-3lo Oct 10, 2025
68dedc0
Feature - Batch Email Send (#95)
dr-3lo Oct 13, 2025
1c7b1ea
Feature - Batch Email Send (#95)
dr-3lo Oct 13, 2025
c5f73f6
Coderabbit comments fix
dr-3lo Oct 13, 2025
7cbeecb
Fixes for PR #158 comments:
dr-3lo Oct 14, 2025
64a6f28
Add validation tests for empty recipients in BatchEmailRequest and Se…
dr-3lo Oct 14, 2025
f31e167
using consistent assertion style.
dr-3lo Oct 14, 2025
a363f83
Refactor email request validation and documentation for clarity and c…
dr-3lo Oct 14, 2025
97038d1
Remove redundant test case for zero total recipients in "exceed" vali…
dr-3lo Oct 14, 2025
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
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@
<PackageVersion Include="FluentAssertions" Version="7.2.0" />
<PackageVersion Include="FluentAssertions.Analyzers" Version="0.34.1" />
</ItemGroup>
</Project>
</Project>
7 changes: 7 additions & 0 deletions Mailtrap.sln
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mailtrap.Example.ContactEve
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mailtrap.Example.EmailTemplates", "examples\Mailtrap.Example.EmailTemplates\Mailtrap.Example.EmailTemplates.csproj", "{A52169E1-8653-4B7D-9F14-84837E237E43}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mailtrap.Example.Email.BatchSend", "examples\Mailtrap.Example.Email.BatchSend\Mailtrap.Example.Email.BatchSend.csproj", "{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -197,6 +199,10 @@ Global
{A52169E1-8653-4B7D-9F14-84837E237E43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A52169E1-8653-4B7D-9F14-84837E237E43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A52169E1-8653-4B7D-9F14-84837E237E43}.Release|Any CPU.Build.0 = Release|Any CPU
{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -225,6 +231,7 @@ Global
{5CEEEC08-7F1F-4F75-BC8D-6E80C54C21FD} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
{AA91FC07-FE50-4DE1-A388-7AA0DB35C84A} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
{A52169E1-8653-4B7D-9F14-84837E237E43} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0FF614CC-FEBC-4C66-B3FC-FCB73EE511D7}
Expand Down
2 changes: 1 addition & 1 deletion build/mailtrap-global.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

<PropertyGroup Label="C# Global Project Settings" Condition="'$(MSBuildProjectExtension)' == '.csproj'">
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<LangVersion>13.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand Down
316 changes: 316 additions & 0 deletions docs/cookbook/batch-send-email.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
---
uid: batch-send-email
---

# Sending Batch Emails
This article covers scenarios for forming and sending batch emails using the Mailtrap API client.

## Creating batch request
Batch email API uses @Mailtrap.Emails.Requests.BatchEmailRequest to create a payload for the API request.
You can set up a base request and a collection of individual requests.

### Fluent builder
You can use the fluent builder extensions in @Mailtrap.Emails.Requests.BatchEmailRequestBuilder to create batch requests in a fluent style:
```csharp
using Mailtrap.Emails.Requests;

...

var batchRequest = BatchEmailRequest
.Create()
.Base(b => b
.From("[email protected]", "John Doe")
.Subject("Batch Invitation"))
.Requests(r => r
.To("[email protected]")
.Text("Dear Bill,\n\nSee you soon!"))
.Requests(SendEmailRequest.Create()
.From("[email protected]", " It's a John Cena") // Overwrite the base attributes
.To("[email protected]")
.Cc("[email protected]")
.Subject("Invitation to Earth"));
...
```

### Regular initialization
Alternatively, you can use object initialization:
```csharp
using Mailtrap.Emails.Requests;

...

var baseRequest = new EmailRequest
{
From = new EmailAddress("[email protected]", "John Doe"),
Subject = "Batch Invitation"
};

// You can specify up to 1000 recipients in total across the To, Cc, and Bcc fields (i.e. To + CC + Bcc <=1000).
// At least one of recipient collections must contain at least one recipient.
var requests = new List<SendEmailRequest>
{
new SendEmailRequest
{
To = { new EmailAddress("[email protected]") },
TextBody = "Dear Bill,\n\nSee you soon!"
},
new SendEmailRequest
{
Cc = { new EmailAddress("[email protected]") },
TextBody = "Dear Lord,\n\nSee you soon!"
},
new SendEmailRequest
{
Bcc = { new EmailAddress("[email protected]") },
TextBody = "Dear Sara,\n\nKeep you posted!"
}
};

var batchRequest = new BatchEmailRequest
{
Base = baseRequest,
Requests = requests
};
```

> [!NOTE]
> You can specify up to 500 requests in a single batch.
> Each request will inherit properties from the base request unless overridden.

## Attaching files
You can attach files to Base and individual requests in the batch, just as with single emails in two ways for inlining (embedding) or as a standalone downloadable file:
```csharp
using Mailtrap.Emails.Requests;

...

var batchRequest = BatchEmailRequest
.Create()
.Base(b => b
.From("[email protected]", "John Doe")
.Subject("Latest Notes")
.Attach(
content: Convert.ToBase64String(File.ReadAllBytes(@"C:\files\note.pdf")),
fileName: "note.pdf",
disposition: DispositionType.Attachment, // Downloadable
mimeType: MediaTypeNames.Application.Pdf))
.Requests(r => r
.To("[email protected]")
.Text("Dear Bill,\n\nSee you soon!")
.Attach(
content: Convert.ToBase64String(File.ReadAllBytes(@"C:\files\preview.pdf")),
fileName: "preview.pdf",
disposition: DispositionType.Inline, // For embedding
mimeType: MediaTypeNames.Application.Pdf));
```

## Email from template
You can use templates in batch requests as well.
You can create a template and obtain its ID via Mailtrap.EmailTemplates APIs.
Then you can use it in emails:
```csharp
using Mailtrap.Emails.Requests;

...

var batchRequest = BatchEmailRequest
.Create()
.Base(b => b.From("[email protected]", "John Doe"))
.Requests(r => r
.To("[email protected]")
.Template("60dca11e-0bc2-42ea-91a8-5ff196acb3f9")
.TemplateVariables(new Dictionary<string, string>
{
{ "name", "Bill" },
{ "sender", "John" }
}));
```

## Kitchen sink
Overview of all possible settings for batch requests:
```csharp
using Mailtrap.Emails.Requests;

...

var batchRequest = BatchEmailRequest.Create();

batchRequest.Base(b => b
.From("[email protected]", "John Doe")
.ReplyTo("[email protected]")
.Subject("Batch Invitation"));

// Categorize
batchRequest.Base
.Category("Invitation");

// HTML body.
// At least one of Text or Html body must be specified.
batchRequest.Base
.Html(
"<h2>Greetings, Bill!</h2>" +
"<p>It will be a great pleasure to see you on our blue planet next weekend.</p>" +
"<p>Regards,<br/>" +
"John</p>");

// Add custom variables
batchRequest.Base
.CustomVariable("var_key", "var_value");

// Add custom headers
batchRequest.Base
.Header(
new("X-Custom-Header-1", "Custom Value 1"),
new("X-Custom-Header-2", "Custom Value 2"),
new("X-Custom-Header-3", "Custom Value 3"));

batchRequest.Requests(r => r
.From("[email protected]", "John R. Doe")
.To("[email protected]")
.Cc("[email protected]", "James")
.Bcc(new EmailAddress("[email protected]"), new EmailAddress("[email protected]"))
.Html("<h2>Greetings, Bill!</h2><p>See you soon!</p>")
.Text("Dear Bill,\n\nSee you soon!")
.Attach(
content: Convert.ToBase64String(File.ReadAllBytes(@"C:\files\preview.pdf")),
fileName: "preview.pdf",
disposition: DispositionType.Attachment,
mimeType: MediaTypeNames.Application.Pdf)
.CustomVariable("var_key", "var_value")
.Header("X-Custom-Header", "Custom Value"));
```

## Request validation
After creating a batch request, validate it on a client side to ensure it meets API requirements and sending won't throw validation exceptions it also will minimize unnecessary HTTP round-trips.
@Mailtrap.Emails.Requests.BatchEmailRequest implements @Mailtrap.Core.Validation.IValidatable interface, which can be used to perform that task.

@Mailtrap.Core.Validation.IValidatable.Validate method verifies request data and returns a @Mailtrap.Core.Validation.ValidationResult instance that contains validation result:
```csharp
using Mailtrap.Emails.Requests;

...

var batchRequest = new BatchEmailRequest();

var validationResult = batchRequest.Validate();

if (validationResult.IsValid)
{
// Send
}
else
{
// Handle invalid batch request
}
```

Alternatively, use `EnsureValidity` to throw on validation errors:
```csharp
using Mailtrap.Emails.Requests;

...

try
{
var batchRequest = new BatchEmailRequest();

batchRequest.Validate().EnsureValidity(nameof(batchRequest)); // Will throw if request isn't valid.
}
catch (ArgumentException aex)
{
// Handle validation issues
}
```

> [!NOTE]
> The client validates batch requests before sending.

## Using batch send API
After forming a valid batch request, send it using the batch API:
```csharp
using Mailtrap;
using Mailtrap.Emails.Models;
using Mailtrap.Emails.Requests;
using Mailtrap.Emails.Responses;

...

private readonly IMailtrapClient _mailtrapClient;

try
{
BatchEmailRequest batchRequest = BatchEmailRequest
.Create()
.Base(b => b.From("[email protected]", "John Doe"))
.Requests(r => r.To("[email protected]").Text("Dear Bill,\n\nSee you soon!"));

if (batchRequest.Validate().IsValid)
{
using var cts = new CancellationTokenSource();

BatchSendEmailResponse response = await _mailtrapClient
.BatchEmail()
.Send(batchRequest, cts.Token);

var messageIds = response.MessageIds;
}
else
{
// handle validation issues
}
}
catch (MailtrapApiException mtex)
{
// handle Mailtrap specific exceptions
}
catch (ArgumentException aex)
{
// handle request validation issues
}
catch (JsonException jex)
{
// handle serialization issues
}
catch (HttpRequestException hrex)
{
// handle HTTP errors
}
catch (OperationCanceledException ocex)
{
// handle cancellation
}
catch (Exception ex)
{
// handle other exceptions
}
```

> [!IMPORTANT]
> @Mailtrap.IMailtrapClient.BatchEmail will use the batch send API as defined in client configuration.

Additionally, you can always use specific send API (transactional, bulk or test) explicitly, to route emails to:
```csharp
var inboxId = 12345;
IBatchEmailClient batchEmailClient = _mailtrapClient.BatchTest(inboxId); // Batch Emails will be sent using Email Testing API
// IBatchEmailClient batchEmailClient = _mailtrapClient.BatchTransactional(); // Batch Emails will be sent using Email Sending API
// IBatchEmailClient batchEmailClient = _mailtrapClient.BatchBulk(); // Batch Emails will be sent using Bulk Sending API

var response = await batchEmailClient.Send(request);
```

> [!TIP]
> @Mailtrap.IMailtrapClient.BatchTransactional, @Mailtrap.IMailtrapClient.BatchBulk and @Mailtrap.IMailtrapClient.BatchTest(System.Int64)
> are factory methods that will create new @Mailtrap.Emails.IBatchEmailClient instance every time when called.
> Thus, if you need to perform multiple `Send()` calls to the same endpoint, it will be a good idea
> to spawn client once and then reuse it:
> ```csharp
> IBatchEmailClient batchEmailClient = _mailtrapClient.BatchBulk(); // Caching client instance
>
> foreach(var request in batchRequests)
> {
> var response = await batchEmailClient.Send(request);
> }
>```

## See also
More examples available [Mailtrap .NET examples on GitHub](https://github.com/railsware/mailtrap-dotnet/tree/main/examples).
Loading