Skip to content

Email API

The Email class represents a decrypted email message in VaultSandbox. All emails are automatically decrypted when retrieved, so you can access content, headers, links, and attachments directly.

string Id { get; }

Unique identifier for this email. Use this to reference the email in API calls.

var emails = await inbox.GetEmailsAsync();
Console.WriteLine($"Email ID: {emails[0].Id}");
// Get specific email
var email = await inbox.GetEmailAsync(emails[0].Id);

string InboxId { get; }

The identifier of the parent inbox that received this email.

var email = await inbox.WaitForEmailAsync();
Console.WriteLine($"Inbox ID: {email.InboxId}");

string From { get; }

The sender’s email address.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
Console.WriteLine($"From: {email.From}");
Assert.That(email.From, Is.EqualTo("[email protected]"));

IReadOnlyList<string> To { get; }

List of recipient email addresses.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
Console.WriteLine($"To: {string.Join(", ", email.To)}");
// Check if specific recipient is included
Assert.That(email.To, Does.Contain(inbox.EmailAddress));

string Subject { get; }

The email subject line.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Welcome",
UseRegex = true
});
Console.WriteLine($"Subject: {email.Subject}");
Assert.That(email.Subject, Does.Contain("Welcome"));

string? Text { get; }

Plain text content of the email. May be null if the email only has HTML content.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
if (email.Text is not null)
{
Console.WriteLine("Plain text version:");
Console.WriteLine(email.Text);
// Validate content
Assert.That(email.Text, Does.Contain("Thank you for signing up"));
}

string? Html { get; }

HTML content of the email. May be null if the email only has plain text.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
if (email.Html is not null)
{
Console.WriteLine("HTML version present");
// Validate HTML structure
Assert.That(email.Html, Does.Contain("<a href="));
Assert.That(email.Html, Does.Contain("</html>"));
// Check for specific elements
Assert.That(email.Html, Does.Match(@"<img[^>]+src="""));
}

DateTimeOffset ReceivedAt { get; }

The date and time when the email was received by VaultSandbox.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
Console.WriteLine($"Received at: {email.ReceivedAt:O}");
// Check if email was received recently
var age = DateTimeOffset.UtcNow - email.ReceivedAt;
Assert.That(age.TotalSeconds, Is.LessThan(60)); // Within last minute

bool IsRead { get; }

Whether this email has been marked as read.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
Console.WriteLine($"Read status: {email.IsRead}");
// Mark as read
await inbox.MarkAsReadAsync(email.Id);
// Verify status changed
var updated = await inbox.GetEmailAsync(email.Id);
Assert.That(updated.IsRead, Is.True);

IReadOnlyList<string>? Links { get; }

All URLs automatically extracted from the email content (both text and HTML). May be null if no links were found.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Password Reset",
UseRegex = true
});
if (email.Links is { Count: > 0 })
{
Console.WriteLine($"Found {email.Links.Count} links:");
foreach (var url in email.Links)
{
Console.WriteLine($" - {url}");
}
// Find specific link
var resetLink = email.Links.FirstOrDefault(url => url.Contains("/reset-password"));
Assert.That(resetLink, Is.Not.Null);
Assert.That(resetLink, Does.StartWith("https://"));
// Extract query parameters
var uri = new Uri(resetLink);
var queryParams = System.Web.HttpUtility.ParseQueryString(uri.Query);
var token = queryParams["token"];
Assert.That(token, Is.Not.Null.And.Not.Empty);
}

IReadOnlyDictionary<string, object>? Headers { get; }

All email headers as a key-value dictionary. Values can be strings or complex objects (e.g., arrays for multi-value headers like Received).

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
Console.WriteLine("Headers:");
Console.WriteLine($" Content-Type: {email.Headers.GetValueOrDefault("content-type")}");
Console.WriteLine($" Message-ID: {email.Headers.GetValueOrDefault("message-id")}");
// Check for custom headers
if (email.Headers.TryGetValue("x-custom-header", out var customValue))
{
Console.WriteLine($"Custom header: {customValue}");
}

IReadOnlyList<EmailAttachment>? Attachments { get; }

List of email attachments, automatically decrypted and ready to use. May be null if there are no attachments.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Invoice",
UseRegex = true
});
if (email.Attachments is not null)
{
Console.WriteLine($"Attachments: {email.Attachments.Count}");
foreach (var attachment in email.Attachments)
{
Console.WriteLine($" - {attachment.Filename} ({attachment.Size} bytes)");
Console.WriteLine($" Type: {attachment.ContentType}");
}
// Find PDF attachment
var pdf = email.Attachments.FirstOrDefault(a => a.ContentType == "application/pdf");
if (pdf is not null)
{
await File.WriteAllBytesAsync($"./downloads/{pdf.Filename}", pdf.Content);
Console.WriteLine($"Saved {pdf.Filename}");
}
// Process text attachment
var textFile = email.Attachments.FirstOrDefault(a => a.ContentType.Contains("text"));
if (textFile is not null)
{
var text = System.Text.Encoding.UTF8.GetString(textFile.Content);
Console.WriteLine($"Text content: {text}");
}
// Parse JSON attachment
var jsonFile = email.Attachments.FirstOrDefault(a => a.ContentType.Contains("json"));
if (jsonFile is not null)
{
var json = System.Text.Encoding.UTF8.GetString(jsonFile.Content);
var data = JsonSerializer.Deserialize<JsonDocument>(json);
Console.WriteLine($"JSON data: {data}");
}
}

See the Attachments Guide for more examples.


AuthenticationResults? AuthResults { get; }

Email authentication results including SPF, DKIM, and DMARC validation.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
if (email.AuthResults is not null)
{
// Validate all authentication
var validation = email.AuthResults.Validate();
Console.WriteLine($"Authentication passed: {validation.Passed}");
if (!validation.Passed)
{
Console.WriteLine("Failures:");
foreach (var failure in validation.Failures)
{
Console.WriteLine($" - {failure}");
}
}
// Check individual results
if (email.AuthResults.Spf is not null)
{
Console.WriteLine($"SPF Result: {email.AuthResults.Spf.Result}");
}
if (email.AuthResults.Dkim?.Count > 0)
{
Console.WriteLine($"DKIM Result: {email.AuthResults.Dkim[0].Result}");
}
if (email.AuthResults.Dmarc is not null)
{
Console.WriteLine($"DMARC Result: {email.AuthResults.Dmarc.Result}");
}
}

See the Authentication Guide for more details.


IReadOnlyDictionary<string, object>? Metadata { get; }

Additional metadata associated with the email.

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
if (email.Metadata is not null)
{
Console.WriteLine("Metadata:");
foreach (var (key, value) in email.Metadata)
{
Console.WriteLine($" {key}: {value}");
}
}

Email instances retrieved through an inbox have access to convenience methods that operate on the email directly.

Note: These methods require the email to be retrieved through an inbox (e.g., via GetEmailsAsync(), WaitForEmailAsync(), or WatchAsync()). Emails created manually or deserialized from JSON cannot use these methods.

Marks this email as read.

Task MarkAsReadAsync(CancellationToken cancellationToken = default)
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
// Using the convenience method on Email
await email.MarkAsReadAsync();
// Equivalent to:
// await inbox.MarkAsReadAsync(email.Id);

Deletes this email from the inbox.

Task DeleteAsync(CancellationToken cancellationToken = default)
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
// Process the email...
// Delete using the convenience method
await email.DeleteAsync();
// Equivalent to:
// await inbox.DeleteEmailAsync(email.Id);

Gets the raw MIME source of this email.

Task<string> GetRawAsync(CancellationToken cancellationToken = default)

Task<string> - The raw email content in MIME format

var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
// Get raw source using the convenience method
var raw = await email.GetRawAsync();
// Save to .eml file
await File.WriteAllTextAsync($"email-{email.Id}.eml", raw);
// Equivalent to:
// var raw = await inbox.GetEmailRawAsync(email.Id);

Lightweight email metadata without body content. Returned by GetEmailsMetadataOnlyAsync().

public sealed record EmailMetadata(
string Id,
string From,
string Subject,
DateTimeOffset ReceivedAt,
bool IsRead);
PropertyTypeDescription
IdstringUnique identifier for the email
FromstringSender’s email address
SubjectstringEmail subject line
ReceivedAtDateTimeOffsetWhen the email was received
IsReadboolWhether the email has been marked as read
// Get metadata for all emails (more efficient than full content)
var metadataList = await inbox.GetEmailsMetadataOnlyAsync();
foreach (var metadata in metadataList)
{
Console.WriteLine($"[{(metadata.IsRead ? "Read" : "New")}] {metadata.Subject}");
Console.WriteLine($" From: {metadata.From}");
Console.WriteLine($" Received: {metadata.ReceivedAt:g}");
}
// Fetch full email only when needed
var unreadEmail = metadataList.FirstOrDefault(m => !m.IsRead);
if (unreadEmail is not null)
{
var fullEmail = await inbox.GetEmailAsync(unreadEmail.Id);
Console.WriteLine($"Body: {fullEmail.Text}");
}
Use CaseRecommended Type
Display email list/summaryEmailMetadata
Filter before fetching contentEmailMetadata
Read email body/HTMLEmail
Access attachmentsEmail
Extract linksEmail
Check authentication resultsEmail

Represents an email attachment.

public sealed record EmailAttachment
{
public required string Filename { get; init; }
public required string ContentType { get; init; }
public required long Size { get; init; }
public required byte[] Content { get; init; }
public string? ContentId { get; init; }
public string? ContentDisposition { get; init; }
public string? Checksum { get; init; }
}
PropertyTypeDescription
FilenamestringOriginal file name
ContentTypestringMIME type (e.g., application/pdf)
SizelongSize in bytes
Contentbyte[]Binary content
ContentIdstring?Content-ID for inline attachments
ContentDispositionstring?inline or attachment
Checksumstring?SHA-256 hash of the content
foreach (var attachment in email.Attachments ?? [])
{
Console.WriteLine($"File: {attachment.Filename}");
Console.WriteLine($"Type: {attachment.ContentType}");
Console.WriteLine($"Size: {attachment.Size} bytes");
Console.WriteLine($"Disposition: {attachment.ContentDisposition}");
Console.WriteLine($"Checksum: {attachment.Checksum}");
// Save to disk
await File.WriteAllBytesAsync(attachment.Filename, attachment.Content);
// Verify checksum if available
if (attachment.Checksum is not null)
{
using var sha256 = System.Security.Cryptography.SHA256.Create();
var computedHash = Convert.ToHexString(sha256.ComputeHash(attachment.Content));
Assert.That(computedHash, Is.EqualTo(attachment.Checksum).IgnoreCase);
}
}

Email authentication validation results.

public sealed record AuthenticationResults
{
public SpfResult? Spf { get; init; }
public IReadOnlyList<DkimResult>? Dkim { get; init; }
public DmarcResult? Dmarc { get; init; }
public ReverseDnsResult? ReverseDns { get; init; }
public AuthValidation Validate();
}

SPF (Sender Policy Framework) validation result.

public sealed record SpfResult
{
public required SpfStatus Status { get; init; }
public string? Domain { get; init; }
public string? Ip { get; init; }
public string? Info { get; init; }
}
public enum SpfStatus
{
Pass,
Fail,
SoftFail,
Neutral,
None,
TempError,
PermError
}

DKIM (DomainKeys Identified Mail) validation result.

public sealed record DkimResult
{
public required DkimStatus Status { get; init; }
public string? Domain { get; init; }
public string? Selector { get; init; }
public string? Info { get; init; }
}
public enum DkimStatus
{
Pass,
Fail,
None
}

DMARC (Domain-based Message Authentication) validation result.

public sealed record DmarcResult
{
public required DmarcStatus Status { get; init; }
public DmarcPolicy? Policy { get; init; }
public bool? Aligned { get; init; }
public string? Domain { get; init; }
public string? Info { get; init; }
}
public enum DmarcStatus
{
Pass,
Fail,
None
}
public enum DmarcPolicy
{
None,
Quarantine,
Reject
}

Reverse DNS lookup result.

public sealed record ReverseDnsResult
{
public required ReverseDnsStatus Status { get; init; }
public string? Ip { get; init; }
public string? Hostname { get; init; }
public string? Info { get; init; }
}
public enum ReverseDnsStatus
{
Pass,
Fail,
None
}

Summary of authentication validation.

public sealed record AuthValidation
{
public required bool Passed { get; init; }
public required bool SpfPassed { get; init; }
public required bool DkimPassed { get; init; }
public required bool DmarcPassed { get; init; }
public required bool ReverseDnsPassed { get; init; }
public required IReadOnlyList<string> Failures { get; init; }
}
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
if (email.AuthResults is not null)
{
var validation = email.AuthResults.Validate();
Console.WriteLine($"Overall: {(validation.Passed ? "PASS" : "FAIL")}");
Console.WriteLine($"SPF: {(validation.SpfPassed ? "PASS" : "FAIL")}");
Console.WriteLine($"DKIM: {(validation.DkimPassed ? "PASS" : "FAIL")}");
Console.WriteLine($"DMARC: {(validation.DmarcPassed ? "PASS" : "FAIL")}");
if (!validation.Passed)
{
Console.WriteLine("\nFailures:");
foreach (var failure in validation.Failures)
{
Console.WriteLine($" - {failure}");
}
}
}
using System.Text.Json;
using System.Text.RegularExpressions;
using VaultSandbox.Client;
async Task CompleteEmailExample(CancellationToken cancellationToken)
{
var client = VaultSandboxClientBuilder.Create()
.WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL"))
.WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY"))
.Build();
try
{
var inbox = await client.CreateInboxAsync(cancellationToken: cancellationToken);
Console.WriteLine($"Created inbox: {inbox.EmailAddress}");
// Trigger test email
await SendTestEmailAsync(inbox.EmailAddress);
// Wait for email
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Test",
UseRegex = true
}, cancellationToken);
// Basic info
Console.WriteLine("\n=== Email Details ===");
Console.WriteLine($"ID: {email.Id}");
Console.WriteLine($"From: {email.From}");
Console.WriteLine($"To: {string.Join(", ", email.To)}");
Console.WriteLine($"Subject: {email.Subject}");
Console.WriteLine($"Received: {email.ReceivedAt:O}");
Console.WriteLine($"Read: {email.IsRead}");
// Content
Console.WriteLine("\n=== Content ===");
if (email.Text is not null)
{
Console.WriteLine("Plain text:");
Console.WriteLine(email.Text[..Math.Min(200, email.Text.Length)] + "...");
}
if (email.Html is not null)
{
Console.WriteLine("HTML version present");
}
// Links
Console.WriteLine("\n=== Links ===");
Console.WriteLine($"Found {email.Links?.Count ?? 0} links:");
foreach (var link in email.Links ?? [])
{
Console.WriteLine($" - {link}");
}
// Attachments
Console.WriteLine("\n=== Attachments ===");
if (email.Attachments is not null)
{
Console.WriteLine($"Found {email.Attachments.Count} attachments:");
foreach (var att in email.Attachments)
{
Console.WriteLine($" - {att.Filename} ({att.ContentType}, {att.Size} bytes)");
// Save attachment
await File.WriteAllBytesAsync($"./downloads/{att.Filename}", att.Content, cancellationToken);
Console.WriteLine($" Saved to ./downloads/{att.Filename}");
}
}
// Authentication
Console.WriteLine("\n=== Authentication ===");
if (email.AuthResults is not null)
{
var validation = email.AuthResults.Validate();
Console.WriteLine($"Overall: {(validation.Passed ? "PASS" : "FAIL")}");
Console.WriteLine($"SPF: {validation.SpfPassed}");
Console.WriteLine($"DKIM: {validation.DkimPassed}");
Console.WriteLine($"DMARC: {validation.DmarcPassed}");
if (!validation.Passed)
{
Console.WriteLine($"Failures: {string.Join(", ", validation.Failures)}");
}
}
// Mark as read
await inbox.MarkAsReadAsync(email.Id, cancellationToken);
Console.WriteLine("\nMarked as read");
// Get raw source
var raw = await inbox.GetEmailRawAsync(email.Id, cancellationToken);
await File.WriteAllTextAsync($"email-{email.Id}.eml", raw, cancellationToken);
Console.WriteLine($"Saved raw source to email-{email.Id}.eml");
// Clean up
await client.DeleteInboxAsync(inbox.EmailAddress, cancellationToken);
}
finally
{
if (client is IAsyncDisposable disposable)
{
await disposable.DisposeAsync();
}
}
}