Inbox API
The Inbox type represents a single email inbox in VaultSandbox. It provides methods for managing emails, waiting for new messages, and monitoring in real-time.
Properties
Section titled “Properties”EmailAddress
Section titled “EmailAddress”func (i *Inbox) EmailAddress() stringReturns the email address for this inbox. Use this address to send test emails.
Example
Section titled “Example”inbox, err := client.CreateInbox(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Send email to: %s\n", inbox.EmailAddress())
// Use in your applicationerr = sendWelcomeEmail(inbox.EmailAddress())InboxHash
Section titled “InboxHash”func (i *Inbox) InboxHash() stringReturns the unique identifier (SHA-256 hash of the public key) for this inbox. Used internally for API operations.
Example
Section titled “Example”fmt.Printf("Inbox ID: %s\n", inbox.InboxHash())ExpiresAt
Section titled “ExpiresAt”func (i *Inbox) ExpiresAt() time.TimeReturns the time when this inbox will expire and be automatically deleted.
Example
Section titled “Example”inbox, err := client.CreateInbox(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Inbox expires at: %s\n", inbox.ExpiresAt().Format(time.RFC3339))
timeUntilExpiry := time.Until(inbox.ExpiresAt())fmt.Printf("Time remaining: %v\n", timeUntilExpiry.Round(time.Second))IsExpired
Section titled “IsExpired”func (i *Inbox) IsExpired() boolReturns whether the inbox has expired.
Example
Section titled “Example”if inbox.IsExpired() { fmt.Println("Inbox has expired")}Methods
Section titled “Methods”GetEmails
Section titled “GetEmails”Lists all emails in the inbox with full content. Emails are automatically decrypted.
func (i *Inbox) GetEmails(ctx context.Context) ([]*Email, error)Returns
Section titled “Returns”[]*Email - Slice of decrypted email objects with full content, sorted by received time (newest first)
Example
Section titled “Example”emails, err := inbox.GetEmails(ctx)if err != nil { log.Fatal(err)}
fmt.Printf("Inbox has %d emails\n", len(emails))
for _, email := range emails { fmt.Printf("- %s from %s\n", email.Subject, email.From)}GetEmailsMetadataOnly
Section titled “GetEmailsMetadataOnly”Lists all emails in the inbox with metadata only (no body or attachments). This is more efficient when you only need to display email summaries.
func (i *Inbox) GetEmailsMetadataOnly(ctx context.Context) ([]*EmailMetadata, error)Returns
Section titled “Returns”[]*EmailMetadata - Slice of email metadata objects, sorted by received time (newest first)
type EmailMetadata struct { ID string From string Subject string ReceivedAt time.Time IsRead bool}Example
Section titled “Example”// Efficient listing for UI displayemails, err := inbox.GetEmailsMetadataOnly(ctx)if err != nil { log.Fatal(err)}
fmt.Printf("Inbox has %d emails\n", len(emails))
for _, email := range emails { status := " " if email.IsRead { status = "✓" } fmt.Printf("[%s] %s - %s (%s)\n", status, email.From, email.Subject, email.ReceivedAt.Format(time.RFC822))}
// Fetch full content only when neededif len(emails) > 0 { fullEmail, err := inbox.GetEmail(ctx, emails[0].ID) if err != nil { log.Fatal(err) } fmt.Printf("Body: %s\n", fullEmail.Text)}When to Use
Section titled “When to Use”Use GetEmailsMetadataOnly instead of GetEmails when:
- Displaying email lists in a UI
- Checking email counts or subjects without reading content
- Implementing pagination or lazy loading
- Reducing bandwidth and processing time
GetEmail
Section titled “GetEmail”Retrieves a specific email by ID.
func (i *Inbox) GetEmail(ctx context.Context, emailID string) (*Email, error)Parameters
Section titled “Parameters”emailID: The unique identifier for the email
Returns
Section titled “Returns”*Email - The decrypted email object
Example
Section titled “Example”emails, err := inbox.GetEmails(ctx)if err != nil { log.Fatal(err)}
firstEmail, err := inbox.GetEmail(ctx, emails[0].ID)if err != nil { log.Fatal(err)}
fmt.Printf("Subject: %s\n", firstEmail.Subject)fmt.Printf("Body: %s\n", firstEmail.Text)Errors
Section titled “Errors”ErrEmailNotFound- Email does not exist
WaitForEmail
Section titled “WaitForEmail”Waits for an email matching specified criteria. This is the recommended way to handle email arrival in tests.
func (i *Inbox) WaitForEmail(ctx context.Context, opts ...WaitOption) (*Email, error)Options
Section titled “Options”| Option | Description |
|---|---|
WithWaitTimeout(d time.Duration) | Maximum time to wait (default: 60s) |
WithSubject(s string) | Filter by exact subject match |
WithSubjectRegex(r *regexp.Regexp) | Filter by subject pattern |
WithFrom(s string) | Filter by exact sender address |
WithFromRegex(r *regexp.Regexp) | Filter by sender pattern |
WithPredicate(fn func(*Email) bool) | Custom filter function |
Returns
Section titled “Returns”*Email - The first email matching the criteria
Examples
Section titled “Examples”// Wait for any emailemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second),)
// Wait for email with specific subjectemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second), vaultsandbox.WithSubjectRegex(regexp.MustCompile(`Password Reset`)),)
// Wait for email from specific senderemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second),)
// Wait with custom predicateemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(15*time.Second), vaultsandbox.WithPredicate(func(e *vaultsandbox.Email) bool { for _, to := range e.To { return true } } return false }),)
// Combine multiple filtersemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second), vaultsandbox.WithSubjectRegex(regexp.MustCompile(`Welcome`)), vaultsandbox.WithFromRegex(regexp.MustCompile(`noreply@`)), vaultsandbox.WithPredicate(func(e *vaultsandbox.Email) bool { return len(e.Links) > 0 }),)Errors
Section titled “Errors”context.DeadlineExceeded- No matching email received within timeout period
WaitForEmailCount
Section titled “WaitForEmailCount”Waits until the inbox has at least the specified number of emails. More efficient than using arbitrary sleeps when testing multiple emails.
func (i *Inbox) WaitForEmailCount(ctx context.Context, count int, opts ...WaitOption) ([]*Email, error)Parameters
Section titled “Parameters”count: Minimum number of emails to wait for
Options
Section titled “Options”| Option | Description |
|---|---|
WithWaitTimeout(d time.Duration) | Maximum time to wait (default: 60s) |
WithSubject(s string) | Filter by exact subject match |
WithSubjectRegex(r *regexp.Regexp) | Filter by subject pattern |
WithFrom(s string) | Filter by exact sender address |
WithFromRegex(r *regexp.Regexp) | Filter by sender pattern |
WithPredicate(fn func(*Email) bool) | Custom filter function |
Returns
Section titled “Returns”[]*Email - All matching emails in the inbox once count is reached
Example
Section titled “Example”// Trigger multiple emailserr := sendMultipleNotifications(inbox.EmailAddress(), 3)if err != nil { log.Fatal(err)}
// Wait for all 3 to arriveemails, err := inbox.WaitForEmailCount(ctx, 3, vaultsandbox.WithWaitTimeout(30*time.Second),)if err != nil { log.Fatal(err)}
// Now process all emailsif len(emails) != 3 { log.Fatalf("expected 3 emails, got %d", len(emails))}Errors
Section titled “Errors”context.DeadlineExceeded- Required count not reached within timeout
Returns a channel that receives emails as they arrive. The channel closes when the context is cancelled.
func (i *Inbox) Watch(ctx context.Context) <-chan *EmailParameters
Section titled “Parameters”ctx: Context for cancellation - when cancelled, the channel closes
Returns
Section titled “Returns”<-chan *Email- Receive-only channel of emails
Example
Section titled “Example”inbox, err := client.CreateInbox(ctx)if err != nil { log.Fatal(err)}
fmt.Printf("Watching: %s\n", inbox.EmailAddress())
watchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)defer cancel()
for email := range inbox.Watch(watchCtx) { fmt.Printf("New email: %q\n", email.Subject) fmt.Printf("From: %s\n", email.From)}Behavior
Section titled “Behavior”- Channel has buffer size of 16
- Non-blocking sends: if buffer is full, events may be dropped
- Channel closes automatically when context is cancelled
- Watcher is automatically unregistered on context cancellation
Best Practice
Section titled “Best Practice”Use context for lifecycle management:
func TestEmailFlow(t *testing.T) { inbox, err := client.CreateInbox(ctx) if err != nil { t.Fatal(err) } defer inbox.Delete(ctx)
watchCtx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel()
for email := range inbox.Watch(watchCtx) { // Process email if foundDesiredEmail(email) { cancel() // Stop watching early break } }}WatchFunc
Section titled “WatchFunc”Calls a callback function for each email as they arrive until the context is cancelled. This is a convenience wrapper around Watch for simpler use cases where you don’t need channel semantics.
func (i *Inbox) WatchFunc(ctx context.Context, fn func(*Email))Parameters
Section titled “Parameters”ctx: Context for cancellation - when cancelled, watching stopsfn: Callback function called for each new email
Example
Section titled “Example”ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)defer cancel()
inbox.WatchFunc(ctx, func(email *vaultsandbox.Email) { fmt.Printf("New email: %s\n", email.Subject) fmt.Printf("From: %s\n", email.From)
// Process the email if strings.Contains(email.Subject, "Password Reset") { processPasswordReset(email) }})Behavior
Section titled “Behavior”- Blocks until context is cancelled
- Each email is passed to the callback function
- Uses
Watchinternally with proper context handling
When to Use
Section titled “When to Use”Use WatchFunc instead of Watch when:
- You prefer callback-style processing over channel iteration
- You want simpler code without channel select statements
- You’re processing emails in a blocking manner
GetSyncStatus
Section titled “GetSyncStatus”Gets the current synchronization status of the inbox with the server.
func (i *Inbox) GetSyncStatus(ctx context.Context) (*SyncStatus, error)Returns
Section titled “Returns”*SyncStatus - Sync status information
type SyncStatus struct { EmailCount int // Number of emails in the inbox EmailsHash string // Hash of the email list for change detection}Example
Section titled “Example”status, err := inbox.GetSyncStatus(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Email count: %d\n", status.EmailCount)fmt.Printf("Emails hash: %s\n", status.EmailsHash)Delete
Section titled “Delete”Deletes this inbox and all its emails.
func (i *Inbox) Delete(ctx context.Context) errorExample
Section titled “Example”inbox, err := client.CreateInbox(ctx)if err != nil { log.Fatal(err)}
// Use inbox...
// Clean uperr = inbox.Delete(ctx)if err != nil { log.Fatal(err)}fmt.Println("Inbox deleted")Best Practice
Section titled “Best Practice”Always delete inboxes after tests using t.Cleanup:
func TestEmailFlow(t *testing.T) { inbox, err := client.CreateInbox(ctx) if err != nil { t.Fatal(err) }
t.Cleanup(func() { inbox.Delete(context.Background()) })
// Test logic...}Export
Section titled “Export”Exports inbox data and encryption keys for backup or sharing.
func (i *Inbox) Export() *ExportedInboxReturns
Section titled “Returns”*ExportedInbox - Serializable inbox data including sensitive keys
type ExportedInbox struct { EmailAddress string `json:"emailAddress"` ExpiresAt time.Time `json:"expiresAt"` InboxHash string `json:"inboxHash"` ServerSigPk string `json:"serverSigPk"` PublicKeyB64 string `json:"publicKeyB64"` SecretKeyB64 string `json:"secretKeyB64"` ExportedAt time.Time `json:"exportedAt"`}Validate
Section titled “Validate”Validates that the exported data is valid before import.
func (e *ExportedInbox) Validate() errorReturns ErrInvalidImportData if the email address is empty or the secret key is missing/invalid.
Example
Section titled “Example”inbox, err := client.CreateInbox(ctx)if err != nil { log.Fatal(err)}data := inbox.Export()
// Save for laterjsonData, err := json.MarshalIndent(data, "", " ")if err != nil { log.Fatal(err)}err = os.WriteFile("inbox-backup.json", jsonData, 0600)if err != nil { log.Fatal(err)}Security Warning
Section titled “Security Warning”Exported data contains private encryption keys. Store securely with restrictive file permissions (0600)!
Complete Inbox Example
Section titled “Complete Inbox Example”package main
import ( "context" "encoding/json" "fmt" "log" "os" "regexp" "time"
"github.com/vaultsandbox/client-go")
func completeInboxExample() error { ctx := context.Background()
client, err := vaultsandbox.New( os.Getenv("VAULTSANDBOX_API_KEY"), vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")), ) if err != nil { return err } defer client.Close()
// Create inbox inbox, err := client.CreateInbox(ctx) if err != nil { return err } fmt.Printf("Created: %s\n", inbox.EmailAddress()) fmt.Printf("Expires: %s\n", inbox.ExpiresAt().Format(time.RFC3339))
// Set up watching in a goroutine watchCtx, cancelWatch := context.WithCancel(ctx) go func() { for email := range inbox.Watch(watchCtx) { fmt.Printf("Received via watch: %s\n", email.Subject) } }()
// Trigger test email err = sendTestEmail(inbox.EmailAddress()) if err != nil { return err }
// Wait for specific email email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second), vaultsandbox.WithSubjectRegex(regexp.MustCompile(`Test`)), ) if err != nil { return err }
fmt.Printf("Found email: %s\n", email.Subject) fmt.Printf("Body: %s\n", email.Text)
// Mark as read err = inbox.MarkEmailAsRead(ctx, email.ID) if err != nil { return err }
// Get all emails allEmails, err := inbox.GetEmails(ctx) if err != nil { return err } fmt.Printf("Total emails: %d\n", len(allEmails))
// Export inbox exportData := inbox.Export() jsonData, err := json.Marshal(exportData) if err != nil { return err } err = os.WriteFile("inbox.json", jsonData, 0600) if err != nil { return err }
// Clean up cancelWatch() err = inbox.Delete(ctx) if err != nil { return err }
return nil}
func main() { if err := completeInboxExample(); err != nil { log.Fatal(err) }}Next Steps
Section titled “Next Steps”- Email API Reference - Work with email objects
- Client API Reference - Learn about client methods
- Waiting for Emails Guide - Best practices
- Real-time Monitoring Guide - Advanced monitoring patterns