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.