C# SDK
.NET 8.0+
NuGet
Send emails with C# using the official Laneful C# SDK. Built for modern .NET applications with comprehensive features and type safety.
Full Feature Support
Templates, attachments, tracking, webhooks
Type Safe
Built-in validation & error handling
Modern C#
C# 12.0+ with async/await
On this page
Prerequisites
To get the most out of this guide, you'll need:
- • .NET 8.0 or higher
- • C# 12.0 or higher
- • A Laneful account with API key
- • Verified domain for sending emails
Installation
NuGet Package
Install the package using the .NET CLI:
dotnet add package Laneful.CSharp
Quick Start
Send your first email in minutes:
Send Simple Text Email
using Laneful;
using Laneful.Models;
using Laneful.Exceptions;
// Create client
var client = new LanefulClient(
"https://your-endpoint.send.laneful.net",
"your-auth-token"
);
// Create email
var email = new Email.Builder()
.From(new Address("sender@example.com", "Your Name"))
.To(new Address("recipient@example.com", "Recipient Name"))
.Subject("Hello from Laneful!")
.TextContent("This is a simple test email sent using the Laneful C# SDK.")
.Build();
// Send email
try
{
var response = await client.SendEmailAsync(email);
Console.WriteLine("✓ Email sent successfully!");
Console.WriteLine($"Response: {response}");
}
catch (ValidationException ex)
{
Console.WriteLine($"✗ Validation error: {ex.Message}");
}
catch (ApiException ex)
{
Console.WriteLine($"✗ API error: {ex.Message}");
Console.WriteLine($"Status code: {ex.StatusCode}");
}
catch (HttpException ex)
{
Console.WriteLine($"✗ HTTP error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"✗ Unexpected error: {ex.Message}");
}Examples
Common use cases and patterns:
HTML Email with Tracking
// Create tracking settings
var tracking = new TrackingSettings(opens: true, clicks: true, unsubscribes: true);
var email = new Email.Builder()
.From(new Address("sender@example.com", "Your Name"))
.To(new Address("recipient@example.com", "Recipient Name"))
.Subject("HTML Email with Tracking")
.HtmlContent("<h1>Welcome!</h1><p>This is an <strong>HTML email</strong> with tracking enabled.</p>")
.TextContent("Welcome! This is an HTML email with tracking enabled.")
.Tracking(tracking)
.Tag("welcome-email")
.Build();
var response = await client.SendEmailAsync(email);Template Email
var email = new Email.Builder()
.From(new Address("sender@example.com"))
.To(new Address("user@example.com"))
.TemplateId("welcome-template")
.TemplateData(new Dictionary<string, object>
{
["name"] = "John Doe",
["company"] = "Acme Corporation",
["activation_link"] = "https://example.com/activate"
})
.Build();
var response = await client.SendEmailAsync(email);Email with Attachments
// Create attachment from file
var attachment = Attachment.FromFile("/path/to/document.pdf");
var email = new Email.Builder()
.From(new Address("sender@example.com"))
.To(new Address("user@example.com"))
.Subject("Document Attached")
.TextContent("Please find the document attached.")
.Attachment(attachment)
.Build();
var response = await client.SendEmailAsync(email);Multiple Recipients with Reply-To
var email = new Email.Builder()
.From(new Address("sender@example.com", "Your Name"))
.To(new Address("user1@example.com", "User One"))
.To(new Address("user2@example.com", "User Two"))
.Cc(new Address("cc@example.com", "CC Recipient"))
.Bcc(new Address("bcc@example.com", "BCC Recipient"))
.ReplyTo(new Address("reply@example.com", "Reply To"))
.Subject("Email to Multiple Recipients")
.TextContent("This email is being sent to multiple recipients.")
.Build();
var response = await client.SendEmailAsync(email);Scheduled Email
// Schedule for 24 hours from now
var sendTime = DateTimeOffset.UtcNow.AddHours(24).ToUnixTimeSeconds();
var email = new Email.Builder()
.From(new Address("sender@example.com"))
.To(new Address("user@example.com"))
.Subject("Scheduled Email")
.TextContent("This email was scheduled.")
.SendTime(sendTime)
.Build();
var response = await client.SendEmailAsync(email);Batch Email Sending
var emails = new[]
{
new Email.Builder()
.From(new Address("sender@example.com"))
.To(new Address("user1@example.com"))
.Subject("Email 1")
.TextContent("First email content.")
.Build(),
new Email.Builder()
.From(new Address("sender@example.com"))
.To(new Address("user2@example.com"))
.Subject("Email 2")
.TextContent("Second email content.")
.Build()
};
var response = await client.SendEmailsAsync(emails);Webhook Handler Example
using Microsoft.AspNetCore.Mvc;
using Laneful.Webhooks;
[ApiController]
[Route("api/[controller]")]
public class WebhookController : ControllerBase
{
private readonly string _webhookSecret;
private readonly ILogger<WebhookController> _logger;
public WebhookController(ILogger<WebhookController> logger)
{
_logger = logger;
_webhookSecret = Environment.GetEnvironmentVariable("LANEFUL_WEBHOOK_SECRET")
?? throw new InvalidOperationException("LANEFUL_WEBHOOK_SECRET is required");
}
[HttpPost("laneful")]
public async Task<IActionResult> HandleWebhook()
{
try
{
// Read raw payload
using var reader = new StreamReader(Request.Body);
var payload = await reader.ReadToEndAsync();
if (string.IsNullOrWhiteSpace(payload))
{
return BadRequest(new { error = "Empty payload received" });
}
// Convert headers to dictionary
var headers = Request.Headers.ToDictionary(
h => h.Key,
h => h.Value.ToString(),
StringComparer.OrdinalIgnoreCase
);
// Extract signature from headers
var signature = WebhookVerifier.ExtractSignatureFromHeaders(headers);
if (string.IsNullOrEmpty(signature))
{
return Unauthorized(new { error = "Missing signature" });
}
// Verify signature
if (!WebhookVerifier.VerifySignature(_webhookSecret, payload, signature))
{
return Unauthorized(new { error = "Invalid signature" });
}
// Parse and validate payload
var webhookData = WebhookVerifier.ParseWebhookPayload(payload);
// Process events
foreach (var eventData in webhookData.Events)
{
var eventType = eventData["event"]?.ToString();
var email = eventData["email"]?.ToString();
switch (eventType)
{
case "delivery":
_logger.LogInformation("Email delivered to: {Email}", email);
break;
case "open":
_logger.LogInformation("Email opened by: {Email}", email);
break;
case "click":
var url = eventData["url"]?.ToString() ?? "Unknown URL";
_logger.LogInformation("Link clicked by {Email}: {Url}", email, url);
break;
case "bounce":
var isHard = eventData["is_hard"] as bool? ?? false;
_logger.LogInformation("Email bounced ({Type}) for: {Email}",
isHard ? "hard" : "soft", email);
break;
}
}
return Ok(new
{
status = "success",
processed = webhookData.Events.Count,
mode = webhookData.IsBatch ? "batch" : "single"
});
}
catch (ArgumentException ex)
{
return BadRequest(new { error = $"Invalid payload: {ex.Message}" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing webhook");
return StatusCode(500, new { error = "Processing error" });
}
}
}API Reference
LanefulClient
Constructors
- •
LanefulClient(string baseUrl, string authToken)- Default timeout (30 seconds) - •
LanefulClient(string baseUrl, string authToken, TimeSpan timeout)- Custom timeout - •
LanefulClient(string baseUrl, string authToken, HttpClient httpClient)- Custom HTTP client - •
LanefulClient(string baseUrl, string authToken, ILogger<LanefulClient> logger)- With logger
Methods
- •
Task<Dictionary<string, object>> SendEmailAsync(Email email)- Send single email - •
Task<Dictionary<string, object>> SendEmailsAsync(IEnumerable<Email> emails)- Send multiple emails
Email.Builder
Required Fields
- •
From(Address from)- Sender address
Optional Fields
- •
To(Address to)/To(string email, string? name)- Recipient addresses - •
Cc(Address cc)/Cc(string email, string? name)- CC addresses - •
Bcc(Address bcc)/Bcc(string email, string? name)- BCC addresses - •
ReplyTo(Address? replyTo)/ReplyTo(string email, string? name)- Reply-to address - •
Subject(string subject)- Email subject - •
TextContent(string? textContent)- Plain text content - •
HtmlContent(string? htmlContent)- HTML content - •
TemplateId(string? templateId)- Template ID - •
TemplateData(Dictionary<string, object>? templateData)- Template data - •
Attachment(Attachment attachment)- File attachments - •
Headers(Dictionary<string, string>? headers)- Custom headers - •
SendTime(long? sendTime)- Scheduled send time (Unix timestamp) - •
WebhookData(Dictionary<string, string>? webhookData)- Webhook data - •
Tag(string? tag)- Email tag for categorization - •
Tracking(TrackingSettings? tracking)- Tracking settings
Utility Classes
- •
Address(string email, string? name)- Email address with optional name - •
Attachment.FromFile(string filePath)- Create attachment from file - •
Attachment(string filename, string contentType, string content)- Create attachment from raw data - •
TrackingSettings(bool opens, bool clicks, bool unsubscribes)- Email tracking configuration
WebhookVerifier
Signature Verification
- •
bool VerifySignature(string secret, string payload, string signature)- Verifies webhook signature (supports sha256= prefix) - •
string GenerateSignature(string secret, string payload)- Generates signature for payload - •
string GenerateSignature(string secret, string payload, bool includePrefix)- Generates signature with optional prefix
Payload Processing
- •
WebhookData ParseWebhookPayload(string payload)- Parse and validate webhook payload structure - •
string GetSignatureHeaderName()- Get the correct header name for webhook signatures - •
string? ExtractSignatureFromHeaders(IDictionary<string, string> headers)- Extract signature from HTTP headers
WebhookData
- •
bool IsBatch- Returns true if payload contains multiple events - •
IReadOnlyList<Dictionary<string, object?>> Events- Returns list of parsed events
Error Handling
Comprehensive error handling with specific exception types:
try
{
var response = await client.SendEmailAsync(email);
Console.WriteLine("✓ Email sent successfully!");
Console.WriteLine($"Response: {response}");
}
catch (ValidationException ex)
{
// Invalid input data
Console.WriteLine($"✗ Validation error: {ex.Message}");
Console.WriteLine("Please check your email configuration");
}
catch (ApiException ex)
{
// API returned an error
Console.WriteLine($"✗ API error: {ex.Message}");
Console.WriteLine($" Status code: {ex.StatusCode}");
Console.WriteLine($" Error message: {ex.ErrorMessage}");
Console.WriteLine("Please check your API credentials and endpoint");
}
catch (HttpException ex)
{
// Network or HTTP-level error
Console.WriteLine($"✗ HTTP error: {ex.Message}");
Console.WriteLine($" Status code: {ex.StatusCode}");
Console.WriteLine("Please check your network connection and endpoint URL");
}
catch (Exception ex)
{
// Other unexpected errors
Console.WriteLine($"✗ Unexpected error: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}Exception Types
- •
ValidationException- Input validation fails - •
ApiException- API returns error response
Properties: StatusCode, ErrorMessage - •
HttpException- HTTP communication fails
Properties: StatusCode - •
LanefulException- Base exception class
Best Practices
- • Always wrap API calls in try-catch
- • Handle specific exception types first
- • Log errors with context information
- • Implement retry logic for transient failures
Webhook Handling
Comprehensive webhook handling with signature verification, payload parsing, and validation:
Basic Signature Verification
using Laneful.Webhooks;
// In your webhook handler
var payload = await request.Body.ReadAsStringAsync(); // Get the raw request body
var signature = request.Headers["x-webhook-signature"].FirstOrDefault();
var secret = "your-webhook-secret";
if (WebhookVerifier.VerifySignature(secret, payload, signature))
{
// Process webhook data
var webhookData = WebhookVerifier.ParseWebhookPayload(payload);
// Handle webhook events
}
else
{
// Invalid signature
return BadRequest();
}Advanced Webhook Processing
// Complete webhook verification and processing workflow
try
{
// Step 1: Get raw payload
var payload = await request.Body.ReadAsStringAsync();
// Step 2: Extract signature from headers (supports multiple formats)
var headers = request.Headers.ToDictionary(
h => h.Key,
h => h.Value.ToString(),
StringComparer.OrdinalIgnoreCase
);
var signature = WebhookVerifier.ExtractSignatureFromHeaders(headers);
// Step 3: Verify signature (supports sha256= prefix)
if (string.IsNullOrEmpty(signature) ||
!WebhookVerifier.VerifySignature(webhookSecret, payload, signature))
{
throw new SecurityException("Invalid webhook signature");
}
// Step 4: Parse and validate payload structure
var webhookData = WebhookVerifier.ParseWebhookPayload(payload);
// Step 5: Process events (handles both batch and single event formats)
foreach (var eventData in webhookData.Events)
{
var eventType = eventData["event"]?.ToString();
var email = eventData["email"]?.ToString();
switch (eventType)
{
case "delivery":
HandleDeliveryEvent(eventData);
break;
case "open":
HandleOpenEvent(eventData);
break;
case "click":
HandleClickEvent(eventData);
break;
case "bounce":
HandleBounceEvent(eventData);
break;
case "drop":
HandleDropEvent(eventData);
break;
case "spam_complaint":
HandleSpamComplaintEvent(eventData);
break;
case "unsubscribe":
HandleUnsubscribeEvent(eventData);
break;
}
}
}
catch (ArgumentException ex)
{
// Payload validation error
return BadRequest(new { error = $"Invalid payload: {ex.Message}" });
}
catch (Exception ex)
{
// Other errors
return Unauthorized(new { error = ex.Message });
}Batch Mode Support
var webhookData = WebhookVerifier.ParseWebhookPayload(payload);
if (webhookData.IsBatch)
{
// Processing multiple events in batch mode
Console.WriteLine($"Processing {webhookData.Events.Count} events in batch");
}
else
{
// Processing single event
Console.WriteLine("Processing single event");
}
// Process all events
foreach (var eventData in webhookData.Events)
{
ProcessEvent(eventData);
}Supported Webhook Events
Delivery Events
- •
delivery- Email delivered successfully - •
bounce- Email bounced (hard or soft) - •
drop- Email dropped (spam, invalid, etc.) - •
spam_complaint- Recipient marked email as spam
Engagement Events
- •
open- Email opened by recipient - •
click- Link clicked in email - •
unsubscribe- Recipient unsubscribed
Webhook Features
Security
- • HMAC-SHA256 signature verification
- • Support for
sha256=prefix - • Constant-time comparison (timing attack protection)
- • Multiple header format support
Validation
- • JSON payload structure validation
- • Required field validation
- • Event type validation
- • Email format validation
- • UUID format validation for lane_id
Ready to Get Started?
Start sending emails with C# in minutes. Check out the examples and integrate with your application.