Email Objects
Email objects in VaultSandbox represent decrypted emails with all their content, headers, and metadata.
Email Structure
Section titled “Email Structure”var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(30)});
Console.WriteLine(email.Id); // "email_abc123"Console.WriteLine(email.Subject); // "Welcome to our service"Console.WriteLine(email.Text); // Plain text contentConsole.WriteLine(email.Html); // HTML contentConsole.WriteLine(email.ReceivedAt); // DateTimeOffsetConsole.WriteLine(email.IsRead); // falseConsole.WriteLine(email.Links); // ["https://example.com/verify"]Console.WriteLine(email.Attachments); // IReadOnlyList<EmailAttachment>Console.WriteLine(email.AuthResults); // SPF/DKIM/DMARC resultsCore Properties
Section titled “Core Properties”Type: string
Unique identifier for the email.
var emailId = email.Id;// Later...var sameEmail = await inbox.GetEmailAsync(emailId);Type: string
Sender’s email address (from the From header).
// Use in assertions (xUnit)Type: IReadOnlyList<string>
List of recipient email addresses.
// Multiple recipientsforeach (var recipient in email.To){ Console.WriteLine(recipient);}
// Check if sent to specific addressAssert.Contains(inbox.EmailAddress, email.To);Subject
Section titled “Subject”Type: string
Email subject line.
Console.WriteLine(email.Subject); // "Password Reset Request"
// Use in filteringvar email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Subject = "Password Reset"});Type: string?
Plain text content of the email.
Console.WriteLine(email.Text);// "Hello,\n\nClick here to reset your password:\nhttps://..."
// May be null if email is HTML-onlyif (email.Text is not null){ Assert.Contains("reset your password", email.Text);}Type: string?
HTML content of the email.
Console.WriteLine(email.Html);// "<html><body><p>Hello,</p><a href='https://...'>Reset Password</a></body></html>"
// May be null if email is plain text onlyif (email.Html is not null){ Assert.Contains("<a href", email.Html);}ReceivedAt
Section titled “ReceivedAt”Type: DateTimeOffset
When the email was received by the gateway.
Console.WriteLine(email.ReceivedAt); // 2024-01-15T12:00:00.000+00:00
// Check if email arrived recentlyvar age = DateTimeOffset.UtcNow - email.ReceivedAt;Assert.True(age < TimeSpan.FromMinutes(1)); // Received within last minuteIsRead
Section titled “IsRead”Type: bool
Whether the email has been marked as read.
Console.WriteLine(email.IsRead); // false
// Mark as read using instance methodawait email.MarkAsReadAsync();
Console.WriteLine(email.IsRead); // trueType: IReadOnlyList<string>?
All URLs extracted from the email (text and HTML).
if (email.Links is not null){ foreach (var link in email.Links) { Console.WriteLine(link); } // https://example.com/verify?token=abc123 // https://example.com/unsubscribe // https://example.com/privacy
// Find specific link var verifyLink = email.Links.FirstOrDefault(url => url.Contains("/verify")); Assert.NotNull(verifyLink);
// Test link using var httpClient = new HttpClient(); var response = await httpClient.GetAsync(verifyLink); Assert.True(response.IsSuccessStatusCode);}Attachments
Section titled “Attachments”Type: IReadOnlyList<EmailAttachment>?
List of email attachments.
if (email.Attachments is not null){ Console.WriteLine($"{email.Attachments.Count} attachments");
foreach (var attachment in email.Attachments) { Console.WriteLine(attachment.Filename); // "invoice.pdf" Console.WriteLine(attachment.ContentType); // "application/pdf" Console.WriteLine(attachment.Size); // 15234 bytes Console.WriteLine(attachment.Content); // byte[] }}See Working with Attachments for details.
AuthResults
Section titled “AuthResults”Type: AuthenticationResults?
Email authentication results (SPF, DKIM, DMARC, reverse DNS).
if (email.AuthResults is not null){ Console.WriteLine($"SPF: {email.AuthResults.Spf?.Result}"); Console.WriteLine($"DKIM: {email.AuthResults.Dkim?.FirstOrDefault()?.Result}"); Console.WriteLine($"DMARC: {email.AuthResults.Dmarc?.Result}");
// Validate all checks var validation = email.AuthResults.Validate(); if (!validation.Passed) { Console.WriteLine($"Authentication failed: {string.Join(", ", validation.Failures)}"); }}See Authentication Results for details.
Headers
Section titled “Headers”Type: IReadOnlyDictionary<string, object>?
All email headers. Values can be strings or complex objects depending on the header.
if (email.Headers is not null){ foreach (var (key, value) in email.Headers) { Console.WriteLine($"{key}: {value}"); } // from: [email protected] // to: [email protected] // subject: Welcome // message-id: <[email protected]> // date: Mon, 15 Jan 2024 12:00:00 +0000 // content-type: text/html; charset=utf-8
// Access specific headers if (email.Headers.TryGetValue("message-id", out var messageId)) { Console.WriteLine($"Message ID: {messageId}"); }}Metadata
Section titled “Metadata”Type: IReadOnlyDictionary<string, object>?
Additional metadata associated with the email.
if (email.Metadata?.TryGetValue("emailSizeBytes", out var size) == true){ Console.WriteLine($"Email size: {size} bytes");}Email Instance Methods
Section titled “Email Instance Methods”Emails retrieved through an inbox have instance methods for common operations:
MarkAsReadAsync
Section titled “MarkAsReadAsync”Mark the email as read.
await email.MarkAsReadAsync();Console.WriteLine(email.IsRead); // trueDeleteAsync
Section titled “DeleteAsync”Delete the email from the inbox.
await email.DeleteAsync();GetRawAsync
Section titled “GetRawAsync”Get the raw email source (decrypted MIME).
var raw = await email.GetRawAsync();Console.WriteLine(raw);// "From: [email protected]\r\nTo: [email protected]\r\n..."Inbox Methods for Emails
Section titled “Inbox Methods for Emails”You can also use inbox methods to work with emails by ID:
GetEmailsAsync
Section titled “GetEmailsAsync”Get all emails with full content.
var emails = await inbox.GetEmailsAsync();foreach (var email in emails){ Console.WriteLine($"{email.Subject}: {email.Text}");}GetEmailsMetadataOnlyAsync
Section titled “GetEmailsMetadataOnlyAsync”Get all emails with metadata only (no body content). More efficient for listing.
var metadataList = await inbox.GetEmailsMetadataOnlyAsync();foreach (var metadata in metadataList){ Console.WriteLine($"[{(metadata.IsRead ? "Read" : "New")}] {metadata.Subject} from {metadata.From}");}
// Fetch full content only when neededvar email = await inbox.GetEmailAsync(metadataList[0].Id);GetEmailAsync
Section titled “GetEmailAsync”Get a specific email by ID.
var email = await inbox.GetEmailAsync(emailId);Console.WriteLine(email.Subject);MarkAsReadAsync
Section titled “MarkAsReadAsync”Mark an email as read via the inbox.
await inbox.MarkAsReadAsync(email.Id);DeleteEmailAsync
Section titled “DeleteEmailAsync”Delete an email from the inbox.
await inbox.DeleteEmailAsync(email.Id);
// Email is now deletedtry{ await inbox.GetEmailAsync(email.Id);}catch (EmailNotFoundException){ Console.WriteLine("Email deleted");}GetEmailRawAsync
Section titled “GetEmailRawAsync”Get the raw source of an email.
var raw = await inbox.GetEmailRawAsync(email.Id);Console.WriteLine(raw);// "From: [email protected]\r\nTo: [email protected]\r\n..."Common Patterns
Section titled “Common Patterns”Content Validation
Section titled “Content Validation”var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Subject = "Welcome", Timeout = TimeSpan.FromSeconds(10)});
// Validate sender
// Validate contentAssert.Contains("Thank you for signing up", email.Text!);Assert.Contains("<h1>Welcome</h1>", email.Html!);
// Validate linksvar verifyLink = email.Links?.FirstOrDefault(url => url.Contains("/verify"));Assert.NotNull(verifyLink);Assert.StartsWith("https://", verifyLink);Link Extraction and Testing
Section titled “Link Extraction and Testing”var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Subject = "Reset"});
// Extract reset linkvar resetLink = email.Links?.FirstOrDefault(url => url.Contains("reset-password") || url.Contains("token="));
Assert.NotNull(resetLink);
// Extract token from linkvar uri = new Uri(resetLink);var query = System.Web.HttpUtility.ParseQueryString(uri.Query);var token = query["token"];
Assert.NotNull(token);Assert.True(token.Length > 20);
// Test the linkusing var httpClient = new HttpClient();var response = await httpClient.GetAsync(resetLink);Assert.Equal(HttpStatusCode.OK, response.StatusCode);Multi-Part Emails
Section titled “Multi-Part Emails”// Email with both text and HTMLif (email.Text is not null && email.Html is not null){ // Validate both versions have key content Assert.Contains("Welcome", email.Text); Assert.Contains("<h1>Welcome</h1>", email.Html);}
// HTML-only emailif (email.Html is not null && email.Text is null){ Console.WriteLine("HTML-only email"); Assert.Contains("<!DOCTYPE html>", email.Html);}
// Plain text onlyif (email.Text is not null && email.Html is null){ Console.WriteLine("Plain text email");}Time-Based Assertions
Section titled “Time-Based Assertions”var startTime = DateTimeOffset.UtcNow;
// Trigger emailawait SendWelcomeEmail(inbox.EmailAddress);
// Wait and receivevar email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(10)});
// Verify it arrived quicklyvar deliveryTime = email.ReceivedAt - startTime;Assert.True(deliveryTime < TimeSpan.FromSeconds(5)); // Within 5 secondsEmail Metadata Analysis
Section titled “Email Metadata Analysis”Console.WriteLine("Email details:");Console.WriteLine($"- From: {email.From}");Console.WriteLine($"- Subject: {email.Subject}");Console.WriteLine($"- Received: {email.ReceivedAt:O}");Console.WriteLine($"- Size: {email.Text?.Length ?? 0} chars");Console.WriteLine($"- Links: {email.Links?.Count ?? 0}");Console.WriteLine($"- Attachments: {email.Attachments?.Count ?? 0}");
// Check email authenticationif (email.AuthResults is not null){ var auth = email.AuthResults.Validate(); Console.WriteLine($"- Auth passed: {auth.Passed}"); if (!auth.Passed) { Console.WriteLine($"- Auth failures: {string.Join(", ", auth.Failures)}"); }}Testing Examples
Section titled “Testing Examples”xUnit Example
Section titled “xUnit Example”public class WelcomeEmailTests : IAsyncLifetime{ private IVaultSandboxClient _client = null!; private IInbox _inbox = null!;
public async Task InitializeAsync() { _client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .Build(); _inbox = await _client.CreateInboxAsync(); }
public async Task DisposeAsync() { await _client.DeleteInboxAsync(_inbox.EmailAddress); await _client.DisposeAsync(); }
[Fact] public async Task Should_Send_Welcome_Email_On_Signup() { await RegisterUser(_inbox.EmailAddress);
var email = await _inbox.WaitForEmailAsync(new WaitForEmailOptions { Subject = "Welcome", Timeout = TimeSpan.FromSeconds(10) });
Assert.Contains("Welcome", email.Subject); Assert.Contains("Thank you for signing up", email.Text!);
var verifyLink = email.Links?.FirstOrDefault(url => url.Contains("/verify")); Assert.NotNull(verifyLink); }
[Fact] public async Task Should_Include_Unsubscribe_Link() { await RegisterUser(_inbox.EmailAddress);
var email = await _inbox.WaitForEmailAsync(new WaitForEmailOptions { Timeout = TimeSpan.FromSeconds(10) });
var unsubLink = email.Links?.FirstOrDefault(url => url.Contains("/unsubscribe") || url.Contains("list-unsubscribe"));
Assert.NotNull(unsubLink); }}NUnit Example
Section titled “NUnit Example”[TestFixture]public class PasswordResetTests{ private IVaultSandboxClient _client = null!; private IInbox _inbox = null!;
[OneTimeSetUp] public async Task SetUp() { _client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .Build(); _inbox = await _client.CreateInboxAsync(); }
[OneTimeTearDown] public async Task TearDown() { await _client.DeleteInboxAsync(_inbox.EmailAddress); await _client.DisposeAsync(); }
[Test] public async Task Should_Send_Reset_Email_With_Valid_Token() { await RequestPasswordReset(_inbox.EmailAddress);
var email = await _inbox.WaitForEmailAsync(new WaitForEmailOptions { Subject = "reset", Timeout = TimeSpan.FromSeconds(10) });
var resetLink = email.Links!.First(); Assert.That(resetLink, Does.StartWith("https://")); Assert.That(resetLink, Does.Contain("token="));
// Verify token format var uri = new Uri(resetLink); var query = System.Web.HttpUtility.ParseQueryString(uri.Query); var token = query["token"]; Assert.That(token, Has.Length.EqualTo(64)); }}Troubleshooting
Section titled “Troubleshooting”Email Content is Null
Section titled “Email Content is Null”if (email.Text is null && email.Html is null){ Console.WriteLine("Email has no content"); Console.WriteLine($"Headers: {string.Join(", ", email.Headers?.Keys ?? [])}");
var raw = await email.GetRawAsync(); Console.WriteLine($"Raw: {raw}");}Links Not Extracted
Section titled “Links Not Extracted”if (email.Links is null || email.Links.Count == 0){ Console.WriteLine("No links found"); Console.WriteLine($"Text: {email.Text}"); Console.WriteLine($"HTML: {email.Html}");
// Manually extract var urlRegex = new Regex(@"https?://[^\s]+", RegexOptions.IgnoreCase); var textLinks = email.Text is not null ? urlRegex.Matches(email.Text).Select(m => m.Value).ToList() : []; Console.WriteLine($"Manual extraction: {string.Join(", ", textLinks)}");}Decryption Errors
Section titled “Decryption Errors”try{ var email = await inbox.GetEmailAsync(emailId);}catch (DecryptionException){ Console.WriteLine("Failed to decrypt email"); Console.WriteLine("This may indicate:"); Console.WriteLine("- Wrong private key"); Console.WriteLine("- Corrupted data"); Console.WriteLine("- Server issue");}Next Steps
Section titled “Next Steps”- Authentication Results - Email authentication details
- Working with Attachments - Handle email attachments
- Email Authentication - Test SPF/DKIM/DMARC
- API Reference: Email - Complete API documentation