Delivery Strategies
VaultSandbox Client supports two email delivery strategies: Server-Sent Events (SSE) for real-time updates and Polling for compatibility. The SDK intelligently chooses the best strategy automatically or allows manual configuration.
Overview
Section titled “Overview”When you wait for emails or watch for new email notifications, the SDK needs to know when emails arrive. It does this using one of two strategies:
- SSE (Server-Sent Events): Real-time push notifications from the server
- Polling: Periodic checking for new emails
Strategy Comparison
Section titled “Strategy Comparison”| Feature | SSE | Polling |
|---|---|---|
| Latency | Near-instant (~100ms) | Poll interval (default: 2s) |
| Server Load | Lower (persistent connection) | Higher (repeated requests) |
| Network Traffic | Lower (only when emails arrive) | Higher (constant polling) |
| Compatibility | Requires persistent connections | Works everywhere |
| Firewall/Proxy | May be blocked | Always works |
| Battery Impact | Lower (push-based) | Higher (constant requests) |
DeliveryStrategy Enum
Section titled “DeliveryStrategy Enum”public enum DeliveryStrategy{ Auto, // Try SSE, fallback to polling (default) Sse, // Server-Sent Events only Polling // Polling only}Auto Strategy (Recommended)
Section titled “Auto Strategy (Recommended)”The default Auto strategy automatically selects the best delivery method:
- Tries SSE first - Attempts to establish an SSE connection
- Falls back to polling - If SSE fails or is unavailable, uses polling
- Adapts to environment - Works seamlessly in different network conditions
using VaultSandbox.Client;
// Auto strategy (default)var client = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UseAutoDelivery() // Default, can be omitted .Build();
// SDK will automatically choose the best strategyvar inbox = await client.CreateInboxAsync();var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(10)});When Auto Chooses SSE
Section titled “When Auto Chooses SSE”- Gateway supports SSE
- Network allows persistent connections
- No restrictive proxy/firewall
When Auto Falls Back to Polling
Section titled “When Auto Falls Back to Polling”- Gateway doesn’t support SSE
- SSE connection fails
- Behind restrictive proxy/firewall
- Network requires periodic reconnection
SSE Strategy
Section titled “SSE Strategy”Server-Sent Events provide real-time push notifications when emails arrive.
Advantages
Section titled “Advantages”- Near-instant delivery: Emails appear within milliseconds
- Lower server load: Single persistent connection
- Efficient: Only transmits when emails arrive
- Battery-friendly: No constant polling
Configuration
Section titled “Configuration”var client = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UseSseDelivery() .WithSseReconnectInterval(TimeSpan.FromSeconds(5)) .WithSseMaxReconnectAttempts(10) .Build();SSE Configuration Options
Section titled “SSE Configuration Options”| Option | Type | Default | Description |
|---|---|---|---|
SseReconnectInterval | TimeSpan | 5 seconds | Initial delay before reconnection |
SseMaxReconnectAttempts | int | 10 | Maximum reconnection attempts |
Reconnection Behavior
Section titled “Reconnection Behavior”SSE uses exponential backoff for reconnections:
1st attempt: SseReconnectInterval (5s)2nd attempt: SseReconnectInterval * 2 (10s)3rd attempt: SseReconnectInterval * 4 (20s)...up to SseMaxReconnectAttemptsExample Usage
Section titled “Example Usage”var client = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UseSseDelivery() .Build();
var inbox = await client.CreateInboxAsync();
// Real-time subscription using IAsyncEnumerable (uses SSE)using var cts = new CancellationTokenSource();
await foreach (var email in inbox.WatchAsync(cts.Token)){ Console.WriteLine($"Instant notification: {email.Subject}");
// Cancel after first email (or based on your logic) if (ShouldStop(email)) { cts.Cancel(); }}
// Waiting also uses SSE (faster than polling)var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(10), Subject = "Welcome", UseRegex = true});When to Use SSE
Section titled “When to Use SSE”- Real-time monitoring: When you need instant email notifications
- Long-running tests: Reduces overall test time
- High email volume: More efficient than polling
- Development/local: Fast feedback during development
Limitations
Section titled “Limitations”- Requires persistent HTTP connection support
- May not work behind some corporate proxies
- Some cloud environments may close long-lived connections
- Requires server-side SSE support
Polling Strategy
Section titled “Polling Strategy”Polling periodically checks for new emails at a configured interval.
Advantages
Section titled “Advantages”- Universal compatibility: Works in all environments
- Firewall-friendly: Standard HTTP requests
- Predictable: Easy to reason about behavior
- Resilient: Automatically recovers from transient failures
Configuration
Section titled “Configuration”var client = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(2)) .Build();Polling Configuration Options
Section titled “Polling Configuration Options”| Option | Type | Default | Description |
|---|---|---|---|
PollInterval | TimeSpan | 2 seconds | How often to poll for emails |
Example Usage
Section titled “Example Usage”var client = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(1)) .Build();
var inbox = await client.CreateInboxAsync();
// Polling-based subscription using IAsyncEnumerableusing var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
await foreach (var email in inbox.WatchAsync(cts.Token)){ Console.WriteLine($"Polled notification: {email.Subject}");}
// Waiting uses polling (checks every PollInterval)var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(10), Subject = "Welcome", UseRegex = true});Choosing Poll Interval
Section titled “Choosing Poll Interval”Different intervals suit different scenarios:
// Fast polling (500ms) - Development/local testingvar fastClient = VaultSandboxClientBuilder.Create() .WithBaseUrl("http://localhost:3000") .WithApiKey("dev-key") .UsePollingDelivery() .WithPollInterval(TimeSpan.FromMilliseconds(500)) .Build();
// Standard polling (2s) - Default, good balancevar standardClient = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(2)) .Build();
// Slow polling (5s) - CI/CD or rate-limited environmentsvar slowClient = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(5)) .Build();When to Use Polling
Section titled “When to Use Polling”- Corporate networks: Restrictive firewall/proxy environments
- CI/CD pipelines: Guaranteed compatibility
- Rate-limited APIs: Avoid hitting request limits
- Debugging: Predictable request timing
- Low email volume: Polling overhead is minimal
Performance Optimization
Section titled “Performance Optimization”For WaitForEmailAsync(), you can override the polling interval per-operation:
// Default client polling: 2svar client = VaultSandboxClientBuilder.Create() .WithBaseUrl("https://smtp.vaultsandbox.com") .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(2)) .Build();
var inbox = await client.CreateInboxAsync();
// Override for specific operation (faster polling)var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(30), PollInterval = TimeSpan.FromSeconds(1) // Check every 1s for this operation});Choosing the Right Strategy
Section titled “Choosing the Right Strategy”Use Auto (Default)
Section titled “Use Auto (Default)”For most use cases, let the SDK choose:
var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL")!) .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) // UseAutoDelivery() is implicit .Build();Best for:
- General testing
- Unknown network conditions
- Mixed environments (dev, staging, CI)
- When you want it to “just work”
Force SSE
Section titled “Force SSE”When you need guaranteed real-time performance:
var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL")!) .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UseSseDelivery() .Build();Best for:
- Local development (known to support SSE)
- Real-time monitoring dashboards
- High-volume email testing
- Latency-sensitive tests
Caveat: Will throw SseException if SSE is unavailable.
Force Polling
Section titled “Force Polling”When compatibility is more important than speed:
var isCI = Environment.GetEnvironmentVariable("CI") == "true";
var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL")!) .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .WithPollInterval(isCI ? TimeSpan.FromSeconds(3) : TimeSpan.FromSeconds(1)) .Build();Best for:
- CI/CD environments (guaranteed to work)
- Corporate networks with restrictive proxies
- When SSE is known to be problematic
- Rate-limited scenarios
Dependency Injection Configuration
Section titled “Dependency Injection Configuration”Via Options
Section titled “Via Options”{ "VaultSandbox": { "BaseUrl": "https://smtp.vaultsandbox.com", "ApiKey": "your-api-key", "DefaultDeliveryStrategy": "Auto", "PollIntervalMs": 2000, "SseReconnectIntervalMs": 5000, "SseMaxReconnectAttempts": 10 }}// Program.cs or Startup.csservices.AddVaultSandboxClient(options =>{ options.BaseUrl = configuration["VaultSandbox:BaseUrl"]!; options.ApiKey = configuration["VaultSandbox:ApiKey"]!; options.DefaultDeliveryStrategy = Enum.Parse<DeliveryStrategy>( configuration["VaultSandbox:DefaultDeliveryStrategy"] ?? "Auto"); options.PollIntervalMs = int.Parse( configuration["VaultSandbox:PollIntervalMs"] ?? "2000"); options.SseReconnectIntervalMs = int.Parse( configuration["VaultSandbox:SseReconnectIntervalMs"] ?? "5000"); options.SseMaxReconnectAttempts = int.Parse( configuration["VaultSandbox:SseMaxReconnectAttempts"] ?? "10");});Via Configuration Binding
Section titled “Via Configuration Binding”services.AddVaultSandboxClient();services.Configure<VaultSandboxClientOptions>( configuration.GetSection("VaultSandbox"));Environment-Specific Configuration
Section titled “Environment-Specific Configuration”Development
Section titled “Development”Fast feedback with SSE:
{ "VaultSandbox": { "BaseUrl": "http://localhost:3000", "DefaultDeliveryStrategy": "Sse" }}// Configuration helperpublic static IVaultSandboxClient CreateClient(IConfiguration configuration){ var builder = VaultSandboxClientBuilder.Create() .WithBaseUrl(configuration["VaultSandbox:BaseUrl"]!) .WithApiKey(configuration["VaultSandbox:ApiKey"]!);
var strategy = configuration["VaultSandbox:DefaultDeliveryStrategy"];
return strategy switch { "Sse" => builder.UseSseDelivery().Build(), "Polling" => builder.UsePollingDelivery().Build(), _ => builder.UseAutoDelivery().Build() };}Reliable polling:
{ "VaultSandbox": { "BaseUrl": "https://smtp.vaultsandbox.com", "DefaultDeliveryStrategy": "Polling", "PollIntervalMs": 3000 }}Production Testing
Section titled “Production Testing”Auto with tuned reconnection:
var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL")!) .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UseAutoDelivery() // SSE config (if available) .WithSseReconnectInterval(TimeSpan.FromSeconds(10)) .WithSseMaxReconnectAttempts(5) // Polling fallback config .WithPollInterval(TimeSpan.FromSeconds(5)) .Build();Error Handling
Section titled “Error Handling”SSE Connection Failures
Section titled “SSE Connection Failures”using VaultSandbox.Client.Exceptions;
try{ var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL")!) .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UseSseDelivery() .Build();
var inbox = await client.CreateInboxAsync(); // Use inbox...}catch (SseException ex){ Console.WriteLine($"SSE failed: {ex.Message}"); Console.WriteLine("Falling back to polling...");
// Recreate with polling var fallbackClient = VaultSandboxClientBuilder.Create() .WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL")!) .WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY")!) .UsePollingDelivery() .Build();
var inbox = await fallbackClient.CreateInboxAsync(); // Continue with polling...}Handling Watch Errors
Section titled “Handling Watch Errors”try{ await foreach (var email in inbox.WatchAsync(cancellationToken)) { await ProcessEmailAsync(email); }}catch (SseException ex){ Console.WriteLine($"SSE error: {ex.Message}"); // Strategy will automatically fallback if using Auto}catch (StrategyException ex){ Console.WriteLine($"Strategy error: {ex.Message}");}catch (OperationCanceledException){ Console.WriteLine("Watch cancelled");}Polling Too Slow
Section titled “Polling Too Slow”If emails arrive slowly with polling:
// Problem: Default 2s polling is too slowvar client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(2)) .Build();
// Solution 1: Faster pollingvar fasterClient = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromMilliseconds(500)) .Build();
// Solution 2: Use SSE if availablevar sseClient = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UseSseDelivery() .Build();
// Solution 3: Override poll interval for specific waitvar email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(10), PollInterval = TimeSpan.FromMilliseconds(500) // Fast polling for this operation});Best Practices
Section titled “Best Practices”1. Use Auto Strategy by Default
Section titled “1. Use Auto Strategy by Default”Let the SDK choose unless you have specific requirements:
// Good: Let SDK choosevar client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .Build();
// Only specify when neededvar ciClient = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UsePollingDelivery() // CI needs guaranteed compatibility .Build();2. Tune for Environment
Section titled “2. Tune for Environment”Configure differently for each environment:
public static IVaultSandboxClient CreateClient(IConfiguration config){ var builder = VaultSandboxClientBuilder.Create() .WithBaseUrl(config["VaultSandbox:BaseUrl"]!) .WithApiKey(config["VaultSandbox:ApiKey"]!);
var env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT"); var isCI = Environment.GetEnvironmentVariable("CI") == "true";
if (isCI) { // CI: Reliable polling return builder .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(3)) .Build(); } else if (env == "Development") { // Dev: Fast SSE return builder .UseSseDelivery() .Build(); } else { // Production: Auto with tuning return builder .UseAutoDelivery() .WithSseReconnectInterval(TimeSpan.FromSeconds(5)) .WithPollInterval(TimeSpan.FromSeconds(2)) .Build(); }}3. Handle SSE Gracefully
Section titled “3. Handle SSE Gracefully”Always have a fallback if forcing SSE:
async Task<IVaultSandboxClient> CreateClientWithFallbackAsync(){ try { var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UseSseDelivery() .Build();
// Test SSE connectivity var inbox = await client.CreateInboxAsync(); await client.DeleteInboxAsync(inbox.EmailAddress);
return client; } catch (SseException) { Console.WriteLine("SSE unavailable, using polling");
return VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UsePollingDelivery() .Build(); }}4. Don’t Poll Too Aggressively
Section titled “4. Don’t Poll Too Aggressively”Avoid very short polling intervals in production:
// Avoid: Too aggressivevar client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromMilliseconds(100)) // 100ms - too frequent! .Build();
// Good: Reasonable intervalvar client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .UsePollingDelivery() .WithPollInterval(TimeSpan.FromSeconds(2)) // 2s - balanced .Build();5. Use CancellationToken for Long-Running Operations
Section titled “5. Use CancellationToken for Long-Running Operations”Always provide cancellation support for watch operations:
using var cts = new CancellationTokenSource();
// Cancel after timeoutcts.CancelAfter(TimeSpan.FromMinutes(5));
// Or cancel on Ctrl+CConsole.CancelKeyPress += (_, e) =>{ e.Cancel = true; cts.Cancel();};
try{ await foreach (var email in inbox.WatchAsync(cts.Token)) { Console.WriteLine($"Received: {email.Subject}"); }}catch (OperationCanceledException){ Console.WriteLine("Watch operation cancelled");}Next Steps
Section titled “Next Steps”- Real-time Monitoring Guide - Using
WatchAsyncandIAsyncEnumerable - Configuration Reference - All configuration options
- Error Handling - Handle SSE and strategy errors
- CI/CD Integration - Strategy for CI environments