Skip to content

IInbox API

The IInbox interface represents a single email inbox in VaultSandbox. It provides methods for managing emails, waiting for new messages, and monitoring in real-time using IAsyncEnumerable.

string EmailAddress { get; }

The email address for this inbox. Use this address to send test emails.

var inbox = await client.CreateInboxAsync();
Console.WriteLine($"Send email to: {inbox.EmailAddress}");
// Use in your application
await emailService.SendWelcomeEmailAsync(inbox.EmailAddress);

string InboxHash { get; }

Unique identifier for this inbox. Used internally for API operations.

Console.WriteLine($"Inbox ID: {inbox.InboxHash}");

DateTimeOffset ExpiresAt { get; }

The date and time when this inbox will expire and be automatically deleted.

var inbox = await client.CreateInboxAsync();
Console.WriteLine($"Inbox expires at: {inbox.ExpiresAt:O}");
var timeUntilExpiry = inbox.ExpiresAt - DateTimeOffset.UtcNow;
Console.WriteLine($"Time remaining: {timeUntilExpiry.TotalSeconds:F0}s");

bool IsDisposed { get; }

Indicates whether the inbox has been disposed. Once disposed, the inbox instance should no longer be used.

var inbox = await client.CreateInboxAsync();
// Use inbox...
await client.DeleteInboxAsync(inbox.EmailAddress);
if (inbox.IsDisposed)
{
Console.WriteLine("Inbox has been disposed");
}

Lists all emails in the inbox with full content. Emails are automatically decrypted.

Task<IReadOnlyList<Email>> GetEmailsAsync(CancellationToken cancellationToken = default)

Task<IReadOnlyList<Email>> - List of decrypted email records with full content, sorted by received time (newest first)

var emails = await inbox.GetEmailsAsync();
Console.WriteLine($"Inbox has {emails.Count} emails");
foreach (var email in emails)
{
Console.WriteLine($"- {email.Subject} from {email.From}");
Console.WriteLine($" Body: {email.Text?[..Math.Min(100, email.Text?.Length ?? 0)]}...");
}

Lists all emails in the inbox with metadata only (no body content). This is more efficient when you only need basic email information like subject, sender, and received time.

Task<IReadOnlyList<EmailMetadata>> GetEmailsMetadataOnlyAsync(CancellationToken cancellationToken = default)

Task<IReadOnlyList<EmailMetadata>> - List of email metadata records (without body content)

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 just metadata - more efficient for listing
var emailsMetadata = await inbox.GetEmailsMetadataOnlyAsync();
Console.WriteLine($"Inbox has {emailsMetadata.Count} emails");
foreach (var metadata in emailsMetadata)
{
Console.WriteLine($"- [{(metadata.IsRead ? "Read" : "Unread")}] {metadata.Subject}");
Console.WriteLine($" From: {metadata.From} at {metadata.ReceivedAt:g}");
}
// Fetch full content only for emails you need
var importantEmail = emailsMetadata.FirstOrDefault(m => m.Subject.Contains("Important"));
if (importantEmail is not null)
{
var fullEmail = await inbox.GetEmailAsync(importantEmail.Id);
Console.WriteLine($"Body: {fullEmail.Text}");
}

Use GetEmailsMetadataOnlyAsync when:

  • You need to display a list of emails without their content
  • You want to filter emails before fetching full content
  • You’re building a UI that shows email summaries
  • Performance is important and you don’t need email bodies

Use GetEmailsAsync when:

  • You need access to email body (Text, Html)
  • You need attachments, links, or headers
  • You’re processing all emails and need their full content

Retrieves a specific email by ID.

Task<Email> GetEmailAsync(string emailId, CancellationToken cancellationToken = default)
  • emailId: The unique identifier for the email
  • cancellationToken: Cancellation token for the operation

Task<Email> - The decrypted email record

var emails = await inbox.GetEmailsAsync();
var firstEmail = await inbox.GetEmailAsync(emails[0].Id);
Console.WriteLine($"Subject: {firstEmail.Subject}");
Console.WriteLine($"Body: {firstEmail.Text}");
  • EmailNotFoundException - Email does not exist

Gets the current number of emails in the inbox without fetching the full email data.

Task<int> GetEmailCountAsync(CancellationToken cancellationToken = default)

Task<int> - The number of emails in the inbox

var count = await inbox.GetEmailCountAsync();
Console.WriteLine($"Inbox has {count} emails");
// Useful for checking if emails have arrived without fetching all data
if (count > 0)
{
var emails = await inbox.GetEmailsAsync();
// Process emails...
}

Waits for an email matching specified criteria. This is the recommended way to handle email arrival in tests.

Task<Email> WaitForEmailAsync(
WaitForEmailOptions? options = null,
CancellationToken cancellationToken = default)
  • options (optional): Wait configuration options
  • cancellationToken: Cancellation token for the operation
public sealed class WaitForEmailOptions
{
public TimeSpan? Timeout { get; set; }
public TimeSpan? PollInterval { get; set; }
public string? Subject { get; set; }
public string? From { get; set; }
public bool UseRegex { get; set; }
public Func<Email, bool>? Predicate { get; set; }
}
PropertyTypeDefaultDescription
TimeoutTimeSpan?30sMaximum time to wait
PollIntervalTimeSpan?2sPolling interval
Subjectstring?-Filter by subject (exact match or regex based on UseRegex)
Fromstring?-Filter by sender address (exact match or regex based on UseRegex)
UseRegexboolfalseWhen true, Subject and From are treated as regex patterns
PredicateFunc<Email, bool>?-Custom filter function

Task<Email> - The first email matching the criteria

// Wait for any email
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10)
});
// Wait for email with specific subject (exact match)
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Password Reset"
});
// Wait for email with subject pattern (regex)
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Password Reset",
UseRegex = true
});
// Wait for email from specific sender
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
});
// Wait with custom predicate
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(15),
Predicate = e => e.To.Contains("[email protected]")
});
// Combine multiple filters
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Welcome",
From = "noreply@",
UseRegex = true,
Predicate = e => e.Links.Count > 0
});
  • VaultSandboxTimeoutException - No matching email received within timeout period

Waits until the inbox has at least the specified number of emails. More efficient than using arbitrary timeouts when testing multiple emails.

Task WaitForEmailCountAsync(
int count,
WaitForEmailCountOptions? options = null,
CancellationToken cancellationToken = default)
  • count: Minimum number of emails to wait for
  • options (optional): Wait configuration options
  • cancellationToken: Cancellation token for the operation
public sealed class WaitForEmailCountOptions
{
public TimeSpan? Timeout { get; set; }
}
PropertyTypeDefaultDescription
TimeoutTimeSpan?30sMaximum time to wait

Task - Completes when the inbox has at least the specified number of emails

// Trigger multiple emails
await notificationService.SendMultipleNotificationsAsync(inbox.EmailAddress, 3);
// Wait for all 3 to arrive
await inbox.WaitForEmailCountAsync(3, new WaitForEmailCountOptions
{
Timeout = TimeSpan.FromSeconds(30)
});
// Now process all emails
var emails = await inbox.GetEmailsAsync();
Assert.That(emails.Count, Is.GreaterThanOrEqualTo(3));
  • VaultSandboxTimeoutException - Required count not reached within timeout

Streams new emails in real-time as they arrive. This is the .NET equivalent of event-based subscriptions.

IAsyncEnumerable<Email> WatchAsync(CancellationToken cancellationToken = default)

IAsyncEnumerable<Email> - Async stream of emails as they arrive

var inbox = await client.CreateInboxAsync();
Console.WriteLine($"Monitoring: {inbox.EmailAddress}");
// Stream new emails using await foreach
await foreach (var email in inbox.WatchAsync(cancellationToken))
{
Console.WriteLine($"New email: \"{email.Subject}\"");
Console.WriteLine($"From: {email.From}");
// Process email...
if (email.Subject.Contains("Stop"))
{
break; // Exit the stream
}
}

Use a CancellationToken to control the stream lifetime:

using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
await foreach (var email in inbox.WatchAsync(cts.Token))
{
Console.WriteLine($"Received: {email.Subject}");
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Monitoring stopped");
}

Gets the current synchronization status of the inbox with the server.

Task<InboxSyncStatus> GetSyncStatusAsync(CancellationToken cancellationToken = default)

Task<InboxSyncStatus> - Sync status information

public sealed record InboxSyncStatus
{
public required int EmailCount { get; init; }
public required string EmailsHash { get; init; }
}
var status = await inbox.GetSyncStatusAsync();
Console.WriteLine($"Email count: {status.EmailCount}");
Console.WriteLine($"Emails hash: {status.EmailsHash}");

Gets the raw, decrypted source of a specific email (original MIME format).

Task<string> GetEmailRawAsync(string emailId, CancellationToken cancellationToken = default)
  • emailId: The unique identifier for the email
  • cancellationToken: Cancellation token for the operation

Task<string> - The raw MIME source of the email

var emails = await inbox.GetEmailsAsync();
var raw = await inbox.GetEmailRawAsync(emails[0].Id);
Console.WriteLine("Raw MIME source:");
Console.WriteLine(raw);
// Save to file for debugging
await File.WriteAllTextAsync("email.eml", raw);

Marks a specific email as read.

Task MarkAsReadAsync(string emailId, CancellationToken cancellationToken = default)
  • emailId: The unique identifier for the email
  • cancellationToken: Cancellation token for the operation
var emails = await inbox.GetEmailsAsync();
await inbox.MarkAsReadAsync(emails[0].Id);
Console.WriteLine("Email marked as read");

Deletes a specific email from the inbox.

Task DeleteEmailAsync(string emailId, CancellationToken cancellationToken = default)
  • emailId: The unique identifier for the email
  • cancellationToken: Cancellation token for the operation
var emails = await inbox.GetEmailsAsync();
// Delete first email
await inbox.DeleteEmailAsync(emails[0].Id);
Console.WriteLine("Email deleted");
// Verify deletion
var updated = await inbox.GetEmailsAsync();
Assert.That(updated.Count, Is.EqualTo(emails.Count - 1));

To delete an inbox and all its emails, use the DeleteInboxAsync method on the client:

await client.DeleteInboxAsync(inbox.EmailAddress);
var inbox = await client.CreateInboxAsync();
// Use inbox...
// Clean up
await client.DeleteInboxAsync(inbox.EmailAddress);
Console.WriteLine("Inbox deleted");

Always delete inboxes after tests:

[TearDown]
public async Task Cleanup()
{
if (_inbox is not null)
{
await _client.DeleteInboxAsync(_inbox.EmailAddress);
}
}

Exports inbox data and encryption keys for backup or sharing.

Task<InboxExport> ExportAsync()

Task<InboxExport> - Serializable inbox data including sensitive keys

public sealed record InboxExport
{
public int Version { get; init; } = 1;
public required string EmailAddress { get; init; }
public required DateTimeOffset ExpiresAt { get; init; }
public required string InboxHash { get; init; }
public required string ServerSigPk { get; init; }
public required string SecretKey { get; init; } // Public key derived from bytes 1152-2400
public required DateTimeOffset ExportedAt { get; init; }
}
var inbox = await client.CreateInboxAsync();
var data = await inbox.ExportAsync();
// Save for later
var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync("inbox-backup.json", json);

Exported data contains private encryption keys. Store securely!

using System.Text.Json;
using VaultSandbox.Client;
async Task CompleteInboxExample(CancellationToken cancellationToken)
{
var client = VaultSandboxClientBuilder.Create()
.WithBaseUrl(Environment.GetEnvironmentVariable("VAULTSANDBOX_URL"))
.WithApiKey(Environment.GetEnvironmentVariable("VAULTSANDBOX_API_KEY"))
.Build();
try
{
// Create inbox
var inbox = await client.CreateInboxAsync(cancellationToken: cancellationToken);
Console.WriteLine($"Created: {inbox.EmailAddress}");
Console.WriteLine($"Expires: {inbox.ExpiresAt:O}");
// Start watching for emails in a separate task
var watchTask = Task.Run(async () =>
{
await foreach (var email in inbox.WatchAsync(cancellationToken))
{
Console.WriteLine($"Received: {email.Subject}");
}
}, cancellationToken);
// Trigger test email
await SendTestEmailAsync(inbox.EmailAddress);
// Wait for specific email
var email = await inbox.WaitForEmailAsync(new WaitForEmailOptions
{
Timeout = TimeSpan.FromSeconds(10),
Subject = "Test",
UseRegex = true
}, cancellationToken);
Console.WriteLine($"Found email: {email.Subject}");
Console.WriteLine($"Body: {email.Text}");
// Mark as read
await inbox.MarkAsReadAsync(email.Id, cancellationToken);
// Get all emails
var allEmails = await inbox.GetEmailsAsync(cancellationToken);
Console.WriteLine($"Total emails: {allEmails.Count}");
// Export inbox
var exportData = await inbox.ExportAsync();
var json = JsonSerializer.Serialize(exportData);
await File.WriteAllTextAsync("inbox.json", json, cancellationToken);
// Clean up
await client.DeleteInboxAsync(inbox.EmailAddress, cancellationToken);
}
finally
{
if (client is IAsyncDisposable disposable)
{
await disposable.DisposeAsync();
}
}
}