C# .NET SDK

Modern .NET SDK supporting .NET 6.0+, ASP.NET Core integration, and Blazor compatibility.

✅ Ready v1.0.0 .NET 6.0+

Quick Start

NuGet Installation

# Package Manager Console
Install-Package SparkMailr.SDK

# .NET CLI
dotnet add package SparkMailr.SDK

# PackageReference (in .csproj)
<PackageReference Include="SparkMailr.SDK" Version="1.0.0" />

Basic Usage

using SparkMailr;
using SparkMailr.Models;

// Initialize client
var client = new SparkMailrClient(new SparkMailrOptions 
{
    ApiKey = "your-api-key"
});

try 
{
    // Send email
    var request = new SendEmailRequest
    {
        To = new[] { "user@example.com" },
        From = "noreply@yourapp.com",
        Subject = "Welcome to SparkMailr!",
        Html = "<h1>Hello World</h1>",
        Text = "Hello World"
    };
    
    var response = await client.Emails.SendAsync(request);
    Console.WriteLine($"Email sent: {response.Id}");
}
catch (SparkMailrException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}

Configuration

using SparkMailr;

// Full configuration
var options = new SparkMailrOptions
{
    ApiKey = "your-api-key",
    BaseUrl = "https://api.sparkmailr.com",  // Optional
    Timeout = TimeSpan.FromSeconds(30),      // Request timeout
    MaxRetries = 3,                          // Max retry attempts
    RetryDelay = TimeSpan.FromSeconds(1),    // Delay between retries
    Debug = false                            // Enable debug logging
};

var client = new SparkMailrClient(options);

// Configuration from appsettings.json
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var sparkMailrOptions = new SparkMailrOptions();
configuration.GetSection("SparkMailr").Bind(sparkMailrOptions);

var client = new SparkMailrClient(sparkMailrOptions);

ASP.NET Core Integration

Program.cs Setup (.NET 6+)

using SparkMailr.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add SparkMailr services
builder.Services.AddSparkMailr(options =>
{
    options.ApiKey = builder.Configuration["SparkMailr:ApiKey"];
    options.BaseUrl = builder.Configuration["SparkMailr:BaseUrl"];
    options.Timeout = TimeSpan.FromSeconds(30);
    options.Debug = builder.Environment.IsDevelopment();
});

// Add other services
builder.Services.AddControllers();
builder.Services.AddScoped<IEmailService, EmailService>();

var app = builder.Build();

// Configure pipeline
app.UseRouting();
app.MapControllers();

app.Run();

appsettings.json Configuration

{
  "SparkMailr": {
    "ApiKey": "your-api-key",
    "BaseUrl": "https://api.sparkmailr.com",
    "Timeout": "00:00:30",
    "MaxRetries": 3,
    "Debug": false
  },
  "Logging": {
    "LogLevel": {
      "SparkMailr": "Information"
    }
  }
}

Service Implementation

public interface IEmailService
{
    Task<string> SendWelcomeEmailAsync(string userEmail, string userName);
    Task<string> SendPasswordResetAsync(string userEmail, string resetToken);
    Task<BulkEmailResult> SendBulkEmailsAsync(BulkEmailRequest request);
}

public class EmailService : IEmailService
{
    private readonly ISparkMailrClient _sparkMailrClient;
    private readonly ILogger<EmailService> _logger;
    
    public EmailService(ISparkMailrClient sparkMailrClient, ILogger<EmailService> logger)
    {
        _sparkMailrClient = sparkMailrClient;
        _logger = logger;
    }
    
    public async Task<string> SendWelcomeEmailAsync(string userEmail, string userName)
    {
        try
        {
            var request = new SendEmailRequest
            {
                To = new[] { userEmail },
                From = "noreply@yourapp.com",
                TemplateId = "welcome-template",
                TemplateData = new Dictionary<string, object>
                {
                    ["name"] = userName,
                    ["activationLink"] = "https://app.example.com/activate",
                    ["supportEmail"] = "support@yourapp.com"
                }
            };
            
            var response = await _sparkMailrClient.Emails.SendAsync(request);
            _logger.LogInformation("Welcome email sent to {Email}: {EmailId}", userEmail, response.Id);
            return response.Id;
        }
        catch (SparkMailrException ex)
        {
            _logger.LogError(ex, "Failed to send welcome email to {Email}", userEmail);
            throw new EmailServiceException($"Failed to send welcome email: {ex.Message}", ex);
        }
    }
    
    public async Task<string> SendPasswordResetAsync(string userEmail, string resetToken)
    {
        var request = new SendEmailRequest
        {
            To = new[] { userEmail },
            From = "noreply@yourapp.com",
            Subject = "Password Reset Request",
            TemplateId = "password-reset-template",
            TemplateData = new Dictionary<string, object>
            {
                ["resetLink"] = $"https://app.example.com/reset-password?token={resetToken}",
                ["expirationTime"] = DateTime.UtcNow.AddHours(1).ToString("yyyy-MM-dd HH:mm:ss UTC")
            }
        };
        
        var response = await _sparkMailrClient.Emails.SendAsync(request);
        return response.Id;
    }
}

Controller Implementation

[ApiController]
[Route("api/[controller]")]
public class EmailController : ControllerBase
{
    private readonly IEmailService _emailService;
    private readonly ILogger<EmailController> _logger;
    
    public EmailController(IEmailService emailService, ILogger<EmailController> logger)
    {
        _emailService = emailService;
        _logger = logger;
    }
    
    [HttpPost("send-welcome")]
    public async Task<IActionResult> SendWelcomeEmail([FromBody] SendWelcomeRequest request)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
        
        try
        {
            var emailId = await _emailService.SendWelcomeEmailAsync(request.Email, request.Name);
            return Ok(new { Success = true, EmailId = emailId });
        }
        catch (EmailServiceException ex)
        {
            _logger.LogError(ex, "Failed to send welcome email");
            return StatusCode(500, new { Success = false, Error = ex.Message });
        }
    }
    
    [HttpPost("send-bulk")]
    public async Task<IActionResult> SendBulkEmails([FromBody] BulkEmailRequestDto request)
    {
        var bulkRequest = new BulkEmailRequest
        {
            From = request.From,
            TemplateId = request.TemplateId,
            Recipients = request.Recipients.Select(r => new BulkEmailRecipient
            {
                To = r.To,
                TemplateData = r.TemplateData
            }).ToList()
        };
        
        var result = await _emailService.SendBulkEmailsAsync(bulkRequest);
        return Ok(new { Success = true, BatchId = result.BatchId, Count = result.Count });
    }
    
    [HttpGet("status/{emailId}")]
    public async Task<IActionResult> GetEmailStatus(string emailId)
    {
        try
        {
            var status = await _sparkMailrClient.Emails.GetStatusAsync(emailId);
            return Ok(status);
        }
        catch (SparkMailrNotFoundException)
        {
            return NotFound(new { Error = "Email not found" });
        }
    }
}

Advanced Features

Template Emails with Complex Data

// Send email with complex template data
var templateData = new Dictionary<string, object>
{
    ["user"] = new
    {
        FirstName = "John",
        LastName = "Doe",
        Plan = "Premium",
        JoinDate = DateTime.UtcNow.ToString("yyyy-MM-dd")
    },
    ["features"] = new[] { "Feature 1", "Feature 2", "Feature 3" },
    ["activationLink"] = "https://app.example.com/activate/123",
    ["company"] = new
    {
        Name = "Acme Corp",
        SupportEmail = "support@acme.com"
    }
};

var request = new SendEmailRequest
{
    To = new[] { "user@example.com" },
    From = "noreply@yourapp.com",
    TemplateId = "welcome-template",
    TemplateData = templateData
};

var response = await client.Emails.SendAsync(request);
Console.WriteLine($"Template email sent: {response.Id}");

Bulk Email Sending

// Prepare bulk email recipients
var recipients = new List<BulkEmailRecipient>
{
    new BulkEmailRecipient
    {
        To = "user1@example.com",
        TemplateData = new Dictionary<string, object>
        {
            ["name"] = "User 1",
            ["promoCode"] = "ABC123"
        }
    },
    new BulkEmailRecipient
    {
        To = "user2@example.com",
        TemplateData = new Dictionary<string, object>
        {
            ["name"] = "User 2", 
            ["promoCode"] = "DEF456"
        }
    }
};

var bulkRequest = new BulkEmailRequest
{
    From = "newsletter@yourapp.com",
    TemplateId = "newsletter-template",
    Recipients = recipients
};

var bulkResponse = await client.Emails.SendBulkAsync(bulkRequest);
Console.WriteLine($"Bulk batch: {bulkResponse.BatchId}");

Email Analytics & Tracking

// Get email status
var emailId = "em_1234567890";
var status = await client.Emails.GetStatusAsync(emailId);

Console.WriteLine($"Status: {status.Status}");
Console.WriteLine($"Delivered: {status.DeliveredAt}");
Console.WriteLine($"Opened: {status.OpenedAt}");

// Get analytics for date range
var analyticsRequest = new AnalyticsRequest
{
    StartDate = DateTime.UtcNow.AddDays(-30),
    EndDate = DateTime.UtcNow
};

var analytics = await client.Analytics.GetEmailStatsAsync(analyticsRequest);

Console.WriteLine($"Total sent: {analytics.Sent}");
Console.WriteLine($"Delivery rate: {analytics.DeliveryRate:P}");
Console.WriteLine($"Open rate: {analytics.OpenRate:P}");
Console.WriteLine($"Click rate: {analytics.ClickRate:P}");

Error Handling

using SparkMailr.Exceptions;

try
{
    var request = new SendEmailRequest
    {
        To = new[] { "invalid-email" },
        From = "noreply@yourapp.com",
        Subject = "Test Email"
    };
    
    var response = await client.Emails.SendAsync(request);
}
catch (AuthenticationException ex)
{
    Console.WriteLine($"Authentication failed: {ex.Message}");
}
catch (RateLimitException ex)
{
    Console.WriteLine($"Rate limited - retry after: {ex.RetryAfter} seconds");
    
    // Implement exponential backoff
    await Task.Delay(TimeSpan.FromSeconds(ex.RetryAfter));
    // Retry logic here
}
catch (ValidationException ex)
{
    Console.WriteLine("Validation failed:");
    foreach (var (field, errors) in ex.ValidationErrors)
    {
        Console.WriteLine($"  {field}: {string.Join(", ", errors)}");
    }
}
catch (NetworkException ex)
{
    Console.WriteLine($"Network error: {ex.Message}");
}
catch (SparkMailrException ex)
{
    Console.WriteLine($"SparkMailr error: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Unexpected error: {ex.Message}");
}

Blazor Integration

// Program.cs (Blazor Server)
using SparkMailr.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// Add SparkMailr
builder.Services.AddSparkMailr(options =>
{
    options.ApiKey = builder.Configuration["SparkMailr:ApiKey"];
});

var app = builder.Build();

app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

// ContactForm.razor
@page "/contact"
@inject ISparkMailrClient SparkMailrClient
@inject IJSRuntime JSRuntime

<h3>Contact Form</h3>

<EditForm Model="contactModel" OnValidSubmit="HandleSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    
    <div class="form-group">
        <label for="name">Name:</label>
        <InputText id="name" class="form-control" @bind-Value="contactModel.Name" />
        <ValidationMessage For="@(() => contactModel.Name)" />
    </div>
    
    <div class="form-group">
        <label for="email">Email:</label>
        <InputText id="email" class="form-control" @bind-Value="contactModel.Email" />
        <ValidationMessage For="@(() => contactModel.Email)" />
    </div>
    
    <div class="form-group">
        <label for="message">Message:</label>
        <InputTextArea id="message" class="form-control" @bind-Value="contactModel.Message" />
        <ValidationMessage For="@(() => contactModel.Message)" />
    </div>
    
    <button type="submit" class="btn btn-primary" disabled="@isSubmitting">
        @if (isSubmitting)
        {
            <span class="spinner-border spinner-border-sm" role="status"></span>
            Sending...
        }
        else
        {
            Send Message
        }
    </button>
</EditForm>

@code {
    private ContactModel contactModel = new();
    private bool isSubmitting = false;
    
    private async Task HandleSubmit()
    {
        isSubmitting = true;
        
        try
        {
            var request = new SendEmailRequest
            {
                To = new[] { "admin@yourapp.com" },
                From = "noreply@yourapp.com",
                Subject = $"Contact form from {contactModel.Name}",
                Html = $@"
                    <h2>Contact Form Submission</h2>
                    <p><strong>Name:</strong> {contactModel.Name}</p>
                    <p><strong>Email:</strong> {contactModel.Email}</p>
                    <p><strong>Message:</strong></p>
                    <p>{contactModel.Message}</p>
                "
            };
            
            var response = await SparkMailrClient.Emails.SendAsync(request);
            await JSRuntime.InvokeVoidAsync("alert", "Message sent successfully!");
            
            // Reset form
            contactModel = new ContactModel();
        }
        catch (Exception ex)
        {
            await JSRuntime.InvokeVoidAsync("alert", $"Error: {ex.Message}");
        }
        finally
        {
            isSubmitting = false;
        }
    }
    
    public class ContactModel
    {
        [Required]
        public string Name { get; set; } = "";
        
        [Required, EmailAddress]
        public string Email { get; set; } = "";
        
        [Required]
        public string Message { get; set; } = "";
    }
}

Testing with xUnit

using Xunit;
using Moq;
using FluentAssertions;
using SparkMailr;
using SparkMailr.Models;

public class EmailServiceTests
{
    private readonly Mock<ISparkMailrClient> _mockClient;
    private readonly EmailService _emailService;
    
    public EmailServiceTests()
    {
        _mockClient = new Mock<ISparkMailrClient>();
        var mockLogger = new Mock<ILogger<EmailService>>();
        _emailService = new EmailService(_mockClient.Object, mockLogger.Object);
    }
    
    [Fact]
    public async Task SendWelcomeEmailAsync_ShouldReturnEmailId_WhenSuccessful()
    {
        // Arrange
        var userEmail = "test@example.com";
        var userName = "Test User";
        var expectedEmailId = "em_123456";
        
        var mockEmailsClient = new Mock<IEmailsClient>();
        _mockClient.Setup(x => x.Emails).Returns(mockEmailsClient.Object);
        
        mockEmailsClient
            .Setup(x => x.SendAsync(It.IsAny<SendEmailRequest>()))
            .ReturnsAsync(new EmailResponse { Id = expectedEmailId, Status = "queued" });
        
        // Act
        var result = await _emailService.SendWelcomeEmailAsync(userEmail, userName);
        
        // Assert
        result.Should().Be(expectedEmailId);
        
        mockEmailsClient.Verify(x => x.SendAsync(It.Is<SendEmailRequest>(r =>
            r.To.Contains(userEmail) &&
            r.TemplateId == "welcome-template" &&
            r.TemplateData.ContainsKey("name")
        )), Times.Once);
    }
    
    [Fact]
    public async Task SendWelcomeEmailAsync_ShouldThrowEmailServiceException_WhenSparkMailrFails()
    {
        // Arrange
        var userEmail = "test@example.com";
        var userName = "Test User";
        
        var mockEmailsClient = new Mock<IEmailsClient>();
        _mockClient.Setup(x => x.Emails).Returns(mockEmailsClient.Object);
        
        mockEmailsClient
            .Setup(x => x.SendAsync(It.IsAny<SendEmailRequest>()))
            .ThrowsAsync(new SparkMailrException("API Error"));
        
        // Act & Assert
        await Assert.ThrowsAsync<EmailServiceException>(() => 
            _emailService.SendWelcomeEmailAsync(userEmail, userName));
    }
}

// Integration test with TestServer
public class EmailControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    private readonly HttpClient _client;
    
    public EmailControllerIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
        _client = factory.CreateClient();
    }
    
    [Fact]
    public async Task SendWelcomeEmail_ShouldReturnOk_WhenRequestIsValid()
    {
        // Arrange
        var request = new SendWelcomeRequest
        {
            Email = "test@example.com",
            Name = "Test User"
        };
        
        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        // Act
        var response = await _client.PostAsync("/api/email/send-welcome", content);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        
        var responseContent = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<SendEmailResponse>(responseContent);
        
        result.Success.Should().BeTrue();
        result.EmailId.Should().NotBeNullOrEmpty();
    }
}

Need More Help?

Complete Documentation NuGet Package Contact Support