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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [Unreleased]
- **Stats API** - Added sending statistics functionality.

## [3.0.0] - 2025-11-04

### Features
Expand Down
7 changes: 7 additions & 0 deletions Mailtrap.sln
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mailtrap.Example.ContactExp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mailtrap.Example.Suppression", "examples\Mailtrap.Example.Suppression\Mailtrap.Example.Suppression.csproj", "{11894B20-F780-4FC9-8140-3601EBF54C10}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mailtrap.Example.Stats", "examples\Mailtrap.Example.Stats\Mailtrap.Example.Stats.csproj", "{B3E7A1C2-4D5F-6E8A-9B0C-1D2E3F4A5B6C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -216,6 +218,10 @@ Global
{11894B20-F780-4FC9-8140-3601EBF54C10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11894B20-F780-4FC9-8140-3601EBF54C10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11894B20-F780-4FC9-8140-3601EBF54C10}.Release|Any CPU.Build.0 = Release|Any CPU
{B3E7A1C2-4D5F-6E8A-9B0C-1D2E3F4A5B6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B3E7A1C2-4D5F-6E8A-9B0C-1D2E3F4A5B6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B3E7A1C2-4D5F-6E8A-9B0C-1D2E3F4A5B6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B3E7A1C2-4D5F-6E8A-9B0C-1D2E3F4A5B6C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -247,6 +253,7 @@ Global
{9922946A-03DA-4AC6-8AA6-ADEC5EF2E9EB} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
{8791C343-7B1E-4206-A954-A0CFD573E460} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
{11894B20-F780-4FC9-8140-3601EBF54C10} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
{B3E7A1C2-4D5F-6E8A-9B0C-1D2E3F4A5B6C} = {09E18837-1DDE-4EAF-80EC-DA55557C81EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0FF614CC-FEBC-4C66-B3FC-FCB73EE511D7}
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ private static SendEmailRequest TemplateBasedRequest()
- Contact export management – [`examples/Mailtrap.Example.ContactExports`](examples/Mailtrap.Example.ContactExports/)
- Contact events management – [`examples/Mailtrap.Example.ContactEvents`](examples/Mailtrap.Example.ContactEvents/)

### Stats API

- Email sending statistics – [`examples/Mailtrap.Example.Stats`](examples/Mailtrap.Example.Stats/)

### General

- Email Templates management – [`examples/Mailtrap.Example.EmailTemplates`](examples/Mailtrap.Example.EmailTemplates/)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Project Sdk="Microsoft.NET.Sdk" />
10 changes: 10 additions & 0 deletions examples/Mailtrap.Example.Stats/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"profiles": {
"Project": {
"commandName": "Project",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}
87 changes: 87 additions & 0 deletions examples/Mailtrap.Example.Stats/Stats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Mailtrap;
using Mailtrap.Stats;
using Mailtrap.Stats.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;


HostApplicationBuilder hostBuilder = Host.CreateApplicationBuilder(args);

IConfigurationSection config = hostBuilder.Configuration.GetSection("Mailtrap");

hostBuilder.Services.AddMailtrapClient(config);

using IHost host = hostBuilder.Build();

ILogger<Program> logger = host.Services.GetRequiredService<ILogger<Program>>();
IMailtrapClient mailtrapClient = host.Services.GetRequiredService<IMailtrapClient>();

try
{
var accountId = 12345;

IStatsResource statsResource = mailtrapClient
.Account(accountId)
.Stats();

var filter = new StatsFilter
{
StartDate = "2026-01-01",
EndDate = "2026-01-31"
};

// Get aggregated stats
SendingStats stats = await statsResource.GetStats(filter);
logger.LogInformation("Aggregated Stats - Deliveries: {DeliveryCount}, Opens: {OpenCount}", stats.DeliveryCount, stats.OpenCount);

// Get stats grouped by domains
IList<SendingStatGroup> domainStats = await statsResource.ByDomain(filter);
foreach (SendingStatGroup group in domainStats)
{
logger.LogInformation("Domain {Value}: Deliveries={DeliveryCount}", group.Value, group.Stats.DeliveryCount);
}

// Get stats grouped by categories
IList<SendingStatGroup> categoryStats = await statsResource.ByCategory(filter);
foreach (SendingStatGroup group in categoryStats)
{
logger.LogInformation("Category {Value}: Deliveries={DeliveryCount}", group.Value, group.Stats.DeliveryCount);
}

// Get stats grouped by email service providers
IList<SendingStatGroup> espStats = await statsResource.ByEmailServiceProvider(filter);
foreach (SendingStatGroup group in espStats)
{
logger.LogInformation("ESP {Value}: Deliveries={DeliveryCount}", group.Value, group.Stats.DeliveryCount);
}

// Get stats grouped by date
IList<SendingStatGroup> dateStats = await statsResource.ByDate(filter);
foreach (SendingStatGroup group in dateStats)
{
logger.LogInformation("Date {Value}: Deliveries={DeliveryCount}", group.Value, group.Stats.DeliveryCount);
}

// Get stats with filters
var filteredFilter = new StatsFilter
{
StartDate = "2026-01-01",
EndDate = "2026-01-31"
};
filteredFilter.SendingDomainIds.Add(1);
filteredFilter.SendingDomainIds.Add(2);
filteredFilter.SendingStreams.Add("transactional");
filteredFilter.Categories.Add("Welcome");
filteredFilter.EmailServiceProviders.Add("Gmail");

SendingStats filteredStats = await statsResource.GetStats(filteredFilter);
logger.LogInformation("Filtered Stats - Deliveries: {DeliveryCount}", filteredStats.DeliveryCount);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred during API call.");
Environment.ExitCode = 1;
return;
}
17 changes: 17 additions & 0 deletions examples/Mailtrap.Example.Stats/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"System": "Warning",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Debug"
}
}
},
"Mailtrap": {
"ApiToken": "<API_KEY>"
}
}
10 changes: 10 additions & 0 deletions src/Mailtrap.Abstractions/Accounts/IAccountResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,14 @@ public interface IAccountResource : IRestResource
/// When <paramref name="suppressionId"/> is null or empty.
/// </exception>
public ISuppressionResource Suppression(string suppressionId);


/// <summary>
/// Gets sending statistics resource for the account, represented by this resource instance.
/// </summary>
///
/// <returns>
/// Sending statistics resource for the account, represented by this resource instance.
/// </returns>
public IStatsResource Stats();
}
2 changes: 2 additions & 0 deletions src/Mailtrap.Abstractions/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
global using Mailtrap.SendingDomains;
global using Mailtrap.SendingDomains.Models;
global using Mailtrap.SendingDomains.Requests;
global using Mailtrap.Stats;
global using Mailtrap.Stats.Models;
global using Mailtrap.Suppressions;
global using Mailtrap.Suppressions.Models;
global using Mailtrap.TestingMessages;
Expand Down
88 changes: 88 additions & 0 deletions src/Mailtrap.Abstractions/Stats/IStatsResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace Mailtrap.Stats;


/// <summary>
/// Represents sending statistics resource.
/// </summary>
public interface IStatsResource : IRestResource
{
/// <summary>
/// Gets aggregated sending statistics.
/// </summary>
///
/// <param name="filter">
/// Filter parameters for the stats period.
/// </param>
/// <param name="cancellationToken">
/// Token to control operation cancellation.
/// </param>
///
/// <returns>
/// Aggregated sending statistics.
/// </returns>
public Task<SendingStats> GetStats(StatsFilter filter, CancellationToken cancellationToken = default);

/// <summary>
/// Gets sending statistics grouped by sending domains.
/// </summary>
///
/// <param name="filter">
/// Filter parameters for the stats period.
/// </param>
/// <param name="cancellationToken">
/// Token to control operation cancellation.
/// </param>
///
/// <returns>
/// Collection of sending statistics grouped by sending domain.
/// </returns>
public Task<IList<SendingStatGroup>> ByDomain(StatsFilter filter, CancellationToken cancellationToken = default);

/// <summary>
/// Gets sending statistics grouped by categories.
/// </summary>
///
/// <param name="filter">
/// Filter parameters for the stats period.
/// </param>
/// <param name="cancellationToken">
/// Token to control operation cancellation.
/// </param>
///
/// <returns>
/// Collection of sending statistics grouped by category.
/// </returns>
public Task<IList<SendingStatGroup>> ByCategory(StatsFilter filter, CancellationToken cancellationToken = default);

/// <summary>
/// Gets sending statistics grouped by email service providers.
/// </summary>
///
/// <param name="filter">
/// Filter parameters for the stats period.
/// </param>
/// <param name="cancellationToken">
/// Token to control operation cancellation.
/// </param>
///
/// <returns>
/// Collection of sending statistics grouped by email service provider.
/// </returns>
public Task<IList<SendingStatGroup>> ByEmailServiceProvider(StatsFilter filter, CancellationToken cancellationToken = default);

/// <summary>
/// Gets sending statistics grouped by date.
/// </summary>
///
/// <param name="filter">
/// Filter parameters for the stats period.
/// </param>
/// <param name="cancellationToken">
/// Token to control operation cancellation.
/// </param>
///
/// <returns>
/// Collection of sending statistics grouped by date.
/// </returns>
public Task<IList<SendingStatGroup>> ByDate(StatsFilter filter, CancellationToken cancellationToken = default);
}
23 changes: 23 additions & 0 deletions src/Mailtrap.Abstractions/Stats/Models/SendingStatGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Mailtrap.Stats.Models;


/// <summary>
/// Represents a group of sending statistics with a name/value identifier.
/// </summary>
public sealed record SendingStatGroup
{
/// <summary>
/// Gets the name of the grouping key (e.g., "sending_domain_id", "category", "email_service_provider", "date").
/// </summary>
public string Name { get; set; } = string.Empty;

/// <summary>
/// Gets the value of the grouping key.
/// </summary>
public object Value { get; set; } = string.Empty;

/// <summary>
/// Gets the sending statistics for this group.
/// </summary>
public SendingStats Stats { get; set; } = new();
}
78 changes: 78 additions & 0 deletions src/Mailtrap.Abstractions/Stats/Models/SendingStats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace Mailtrap.Stats.Models;


/// <summary>
/// Represents aggregated sending statistics.
/// </summary>
public sealed record SendingStats
{
/// <summary>
/// Gets the number of delivered emails.
/// </summary>
[JsonPropertyName("delivery_count")]
[JsonPropertyOrder(1)]
public int DeliveryCount { get; set; }

/// <summary>
/// Gets the delivery rate.
/// </summary>
[JsonPropertyName("delivery_rate")]
[JsonPropertyOrder(2)]
public double DeliveryRate { get; set; }

/// <summary>
/// Gets the number of bounced emails.
/// </summary>
[JsonPropertyName("bounce_count")]
[JsonPropertyOrder(3)]
public int BounceCount { get; set; }

/// <summary>
/// Gets the bounce rate.
/// </summary>
[JsonPropertyName("bounce_rate")]
[JsonPropertyOrder(4)]
public double BounceRate { get; set; }

/// <summary>
/// Gets the number of opened emails.
/// </summary>
[JsonPropertyName("open_count")]
[JsonPropertyOrder(5)]
public int OpenCount { get; set; }

/// <summary>
/// Gets the open rate.
/// </summary>
[JsonPropertyName("open_rate")]
[JsonPropertyOrder(6)]
public double OpenRate { get; set; }

/// <summary>
/// Gets the number of clicked emails.
/// </summary>
[JsonPropertyName("click_count")]
[JsonPropertyOrder(7)]
public int ClickCount { get; set; }

/// <summary>
/// Gets the click rate.
/// </summary>
[JsonPropertyName("click_rate")]
[JsonPropertyOrder(8)]
public double ClickRate { get; set; }

/// <summary>
/// Gets the number of spam reports.
/// </summary>
[JsonPropertyName("spam_count")]
[JsonPropertyOrder(9)]
public int SpamCount { get; set; }

/// <summary>
/// Gets the spam rate.
/// </summary>
[JsonPropertyName("spam_rate")]
[JsonPropertyOrder(10)]
public double SpamRate { get; set; }
}
Loading