Inboxes
Inboxes are the core concept in VaultSandbox. Each inbox is an isolated, encrypted email destination with its own unique address and encryption keys.
What is an Inbox?
Section titled “What is an Inbox?”An inbox is a temporary, encrypted email destination that:
- Has a unique email address (e.g.,
a1b2c3d4@mail.example.com) - Uses client-side encryption (ML-KEM-768 keypair)
- Expires automatically after a configurable time-to-live (TTL)
- Is isolated from other inboxes
- Stores emails in memory on the gateway
Creating Inboxes
Section titled “Creating Inboxes”Basic Creation
Section titled “Basic Creation”package main
import ( "context" "fmt" "log"
vaultsandbox "github.com/vaultsandbox/client-go")
func main() { client, err := vaultsandbox.New(apiKey) if err != nil { log.Fatal(err) } defer client.Close()
ctx := context.Background() inbox, err := client.CreateInbox(ctx) if err != nil { log.Fatal(err) }
fmt.Println(inbox.EmailAddress()) // "a1b2c3d4@mail.example.com" fmt.Println(inbox.InboxHash()) // "Rr02MLnP7F0pRVC6QdcpSIeyklqu3PDkYglvsfN7Oss" fmt.Println(inbox.ExpiresAt()) // time.Time}With Options
Section titled “With Options”inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(time.Hour), // 1 hour (default: 1 hour) vaultsandbox.WithEmailAddress("test@mail.example.com"), // Request specific address vaultsandbox.WithEmailAuth(false), // Disable email authentication vaultsandbox.WithEncryption(vaultsandbox.EncryptionModePlain), // Request plain inbox)Note: Requesting a specific email address may fail if it’s already in use. The server will return an error.
Inbox Creation Options
Section titled “Inbox Creation Options”| Option | Type | Description |
|---|---|---|
WithTTL | time.Duration | Time-to-live for the inbox (min: 60s, max: 7 days, default: 1 hour) |
WithEmailAddress | string | Request a specific email address |
WithEmailAuth | bool | Enable/disable email authentication checks (SPF/DKIM/DMARC/PTR) |
WithEncryption | EncryptionMode | Request encrypted or plain inbox |
Encryption Mode
Section titled “Encryption Mode”type EncryptionMode string
const ( EncryptionModeDefault EncryptionMode = "" // Use server default EncryptionModeEncrypted EncryptionMode = "encrypted" // Request encrypted inbox EncryptionModePlain EncryptionMode = "plain" // Request plain inbox)Whether encryption can be overridden depends on the server’s encryption policy. See ServerInfo for details.
Client Options
Section titled “Client Options”When creating a client with vaultsandbox.New(), you can configure various options:
client, err := vaultsandbox.New(apiKey, vaultsandbox.WithBaseURL("https://custom-api.example.com"), vaultsandbox.WithTimeout(30*time.Second), vaultsandbox.WithRetries(3),)Available Client Options
Section titled “Available Client Options”| Option | Description |
|---|---|
WithBaseURL(url string) | Set a custom API base URL (default: https://api.vaultsandbox.com) |
WithHTTPClient(client *http.Client) | Use a custom HTTP client for requests |
WithDeliveryStrategy(strategy DeliveryStrategy) | Set how new emails are delivered (see below) |
WithTimeout(timeout time.Duration) | Set default request timeout (default: 60s) |
WithRetries(count int) | Set number of retry attempts for failed requests |
WithRetryOn(statusCodes []int) | Set HTTP status codes that trigger retries (default: 408, 429, 500, 502, 503, 504) |
WithPollingConfig(cfg PollingConfig) | Set polling configuration (see below) |
Delivery Strategies
Section titled “Delivery Strategies”The DeliveryStrategy type controls how the client receives new email notifications:
const ( // StrategySSE uses Server-Sent Events for real-time push notifications (default) StrategySSE DeliveryStrategy = "sse"
// StrategyPolling uses periodic API calls with exponential backoff StrategyPolling DeliveryStrategy = "polling")// Force polling for environments where SSE is blockedclient, err := vaultsandbox.New(apiKey, vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling),)
// Force SSE for lowest latencyclient, err := vaultsandbox.New(apiKey, vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategySSE),)Polling Configuration
Section titled “Polling Configuration”When using polling delivery strategy, you can fine-tune the polling behavior:
type PollingConfig struct { InitialInterval time.Duration // Starting interval between polls (default: 2s) MaxBackoff time.Duration // Maximum interval after backoff (default: 30s) BackoffMultiplier float64 // Multiplier for exponential backoff (default: 1.5) JitterFactor float64 // Random jitter 0-1 to prevent thundering herd (default: 0.3)}// Configure polling with custom settingsclient, err := vaultsandbox.New(apiKey, vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling), vaultsandbox.WithPollingConfig(vaultsandbox.PollingConfig{ InitialInterval: 1 * time.Second, MaxBackoff: 10 * time.Second, BackoffMultiplier: 2.0, JitterFactor: 0.2, }),)Inbox Properties
Section titled “Inbox Properties”EmailAddress()
Section titled “EmailAddress()”Returns: string
The full email address for this inbox.
fmt.Println(inbox.EmailAddress())// "a1b2c3d4@mail.example.com"Send emails to this address to have them appear in the inbox.
InboxHash()
Section titled “InboxHash()”Returns: string
A unique cryptographic hash identifier for the inbox. This is used internally for encryption and identification purposes.
fmt.Println(inbox.InboxHash())// "Rr02MLnP7F0pRVC6QdcpSIeyklqu3PDkYglvsfN7Oss"Note: This is not the same as the local part of the email address. The email address local part (e.g., a1b2c3d4 in a1b2c3d4@mail.example.com) is different from the InboxHash().
ExpiresAt()
Section titled “ExpiresAt()”Returns: time.Time
When the inbox will automatically expire and be deleted.
fmt.Println(inbox.ExpiresAt())// 2024-01-16 12:00:00 +0000 UTC
// Check if inbox is expiring soonhoursUntilExpiry := time.Until(inbox.ExpiresAt()).Hours()fmt.Printf("Expires in %.1f hours\n", hoursUntilExpiry)IsExpired()
Section titled “IsExpired()”Returns: bool
Checks if the inbox has expired.
if inbox.IsExpired() { fmt.Println("Inbox has expired")}EmailAuth()
Section titled “EmailAuth()”Returns: bool
Returns whether email authentication (SPF, DKIM, DMARC, PTR) is enabled for this inbox.
fmt.Printf("Email auth enabled: %v\n", inbox.EmailAuth())When false, all authentication results will have status "skipped". The Validate() method treats "skipped" as passing (not a failure).
Encrypted()
Section titled “Encrypted()”Returns: bool
Returns whether the inbox uses end-to-end encryption.
if inbox.Encrypted() { fmt.Println("Inbox uses end-to-end encryption")} else { fmt.Println("Inbox uses plain text storage")}When true, the inbox has ML-KEM-768 encryption keys and emails are encrypted. When false, emails are stored as Base64-encoded plain text.
Note: The ServerSigPk field in exported inbox data is only present when encrypted is true.
GetSyncStatus()
Section titled “GetSyncStatus()”Returns: (*SyncStatus, error)
Retrieves the synchronization status of the inbox, including the email count and a hash for efficient change detection.
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)The SyncStatus struct contains:
type SyncStatus struct { EmailCount int // Number of emails in the inbox EmailsHash string // Hash of the email list for change detection}Inbox Lifecycle
Section titled “Inbox Lifecycle”┌─────────────────────────────────────────────────────────┐│ Inbox Lifecycle │└─────────────────────────────────────────────────────────┘
1. Creation client.CreateInbox(ctx) → *Inbox ↓ - Keypair generated client-side - Public key sent to server - Unique email address assigned - TTL timer starts
2. Active ↓ - Receive emails - List/read emails - Wait for emails - Monitor for new emails
3. Expiration (TTL reached) or Manual Deletion ↓ inbox.Delete(ctx) or TTL expires - All emails deleted - Inbox address freed - Keypair destroyedWorking with Inboxes
Section titled “Working with Inboxes”Listing Emails
Section titled “Listing Emails”emails, err := inbox.GetEmails(ctx)if err != nil { log.Fatal(err)}
fmt.Printf("%d emails in inbox\n", len(emails))for _, email := range emails { fmt.Printf("%s: %s\n", email.From, email.Subject)}Getting a Specific Email
Section titled “Getting a Specific Email”email, err := inbox.GetEmail(ctx, "email-id-123")if err != nil { log.Fatal(err)}
fmt.Println(email.Subject)fmt.Println(email.Text)Email Struct Fields
Section titled “Email Struct Fields”The Email struct contains the following fields:
| Field | Type | Description |
|---|---|---|
ID | string | Unique email identifier |
From | string | Sender email address |
To | []string | Recipient email addresses |
Subject | string | Email subject line |
Text | string | Plain text body |
HTML | string | HTML body |
ReceivedAt | time.Time | When the email was received |
Headers | map[string]string | Email headers |
Attachments | []Attachment | File attachments |
Links | []string | Links extracted from the email body |
AuthResults | *authresults.AuthResults | Email authentication results (SPF, DKIM, DMARC) |
IsRead | bool | Whether the email has been marked as read |
Attachment Struct
Section titled “Attachment Struct”The Attachment struct contains the following fields:
| Field | Type | Description |
|---|---|---|
Filename | string | Original filename of the attachment |
ContentType | string | MIME type (e.g., application/pdf, image/png) |
Size | int | Size in bytes |
ContentID | string | Content-ID for inline attachments |
ContentDisposition | string | Disposition type (attachment or inline) |
Content | []byte | Raw attachment content |
Checksum | string | Checksum for integrity verification |
// Working with attachmentsfor _, att := range email.Attachments { fmt.Printf("Filename: %s\n", att.Filename) fmt.Printf("Type: %s\n", att.ContentType) fmt.Printf("Size: %d bytes\n", att.Size)
// Save attachment to file os.WriteFile(att.Filename, att.Content, 0644)}AuthResults Struct
Section titled “AuthResults Struct”The AuthResults struct contains email authentication check results:
type AuthResults struct { SPF *SPFResult // SPF check result DKIM []DKIMResult // DKIM signature results (may have multiple) DMARC *DMARCResult // DMARC policy result ReverseDNS *ReverseDNSResult // Reverse DNS check result}
type SPFResult struct { Result string // pass, fail, softfail, neutral, none, temperror, permerror, skipped Domain string IP string Info string}
type DKIMResult struct { Result string // pass, fail, none, skipped Domain string Selector string Info string}
type DMARCResult struct { Result string // pass, fail, none, skipped Policy string // none, quarantine, reject Aligned bool Domain string Info string}
type ReverseDNSResult struct { Result string // pass, fail, none, skipped IP string Hostname string}Validation Methods:
// Quick check if all primary auth checks passedif email.AuthResults.IsPassing() { fmt.Println("Email authentication passed")}
// Detailed validation with failure messagesvalidation := email.AuthResults.Validate()fmt.Printf("Overall: %v\n", validation.Passed)fmt.Printf("SPF: %v\n", validation.SPFPassed)fmt.Printf("DKIM: %v\n", validation.DKIMPassed)fmt.Printf("DMARC: %v\n", validation.DMARCPassed)fmt.Printf("Reverse DNS: %v\n", validation.ReverseDNSPassed)
if len(validation.Failures) > 0 { fmt.Println("Failures:") for _, f := range validation.Failures { fmt.Printf(" - %s\n", f) }}// Accessing email fieldsfmt.Printf("From: %s\n", email.From)fmt.Printf("Subject: %s\n", email.Subject)fmt.Printf("Received: %s\n", email.ReceivedAt)fmt.Printf("Is Read: %v\n", email.IsRead)fmt.Printf("Attachments: %d\n", len(email.Attachments))
// Check authentication resultsif email.AuthResults != nil { if email.AuthResults.SPF != nil { fmt.Printf("SPF: %s\n", email.AuthResults.SPF.Result) } for _, dkim := range email.AuthResults.DKIM { fmt.Printf("DKIM: %s (domain: %s)\n", dkim.Result, dkim.Domain) } if email.AuthResults.DMARC != nil { fmt.Printf("DMARC: %s\n", email.AuthResults.DMARC.Result) } if email.AuthResults.ReverseDNS != nil { fmt.Printf("Reverse DNS: %s\n", email.AuthResults.ReverseDNS.Result) }
// Use convenience method to check if all auth checks passed // Note: "skipped" status is treated as passing if email.AuthResults.IsPassing() { fmt.Println("All authentication checks passed") }}Standalone Validation Functions:
The authresults package also provides standalone validation functions for granular control:
import "github.com/vaultsandbox/client-go/authresults"
// Validate all authentication results at onceerr := authresults.Validate(email.AuthResults)if err != nil { var valErr *authresults.ValidationError if errors.As(err, &valErr) { fmt.Println("Validation failures:", valErr.Errors) }}
// Validate individual checksif err := authresults.ValidateSPF(email.AuthResults); err != nil { fmt.Println("SPF failed:", err)}
if err := authresults.ValidateDKIM(email.AuthResults); err != nil { fmt.Println("DKIM failed:", err)}
if err := authresults.ValidateDMARC(email.AuthResults); err != nil { fmt.Println("DMARC failed:", err)}
if err := authresults.ValidateReverseDNS(email.AuthResults); err != nil { fmt.Println("Reverse DNS failed:", err)}Available Validation Functions:
| Function | Description |
|---|---|
Validate(results) | Validates all authentication results, returns ValidationError with all failures |
ValidateSPF(results) | Validates only SPF, returns ErrSPFFailed on failure |
ValidateDKIM(results) | Validates DKIM (passes if at least one signature passes), returns ErrDKIMFailed on failure |
ValidateDMARC(results) | Validates only DMARC, returns ErrDMARCFailed on failure |
ValidateReverseDNS(results) | Validates only reverse DNS, returns ErrReverseDNSFailed on failure |
Sentinel Errors in authresults package:
| Error | Description |
|---|---|
ErrSPFFailed | SPF check failed |
ErrDKIMFailed | DKIM check failed |
ErrDMARCFailed | DMARC check failed |
ErrReverseDNSFailed | Reverse DNS check failed |
ErrNoAuthResults | No authentication results available |
Waiting for Emails
Section titled “Waiting for Emails”// Wait for any emailemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(30*time.Second),)
// Wait for specific emailemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(30*time.Second), vaultsandbox.WithSubjectRegex(regexp.MustCompile(`Password Reset`)), vaultsandbox.WithFrom("noreply@example.com"),)Available Wait Options:
| Option | Description |
|---|---|
WithWaitTimeout(duration) | Maximum time to wait for an email |
WithSubject(string) | Filter by exact subject match |
WithSubjectRegex(regexp) | Filter by subject regex pattern |
WithFrom(string) | Filter by exact sender match |
WithFromRegex(regexp) | Filter by sender regex pattern |
WithPredicate(func(*Email) bool) | Filter by custom predicate function |
// Using exact subject matchemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithSubject("Welcome to Our Service"),)
// Using sender regexemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithFromRegex(regexp.MustCompile(`.*@example\.com`)),)
// Using custom predicateemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithPredicate(func(e *vaultsandbox.Email) bool { return len(e.Attachments) > 0 }),)
// Combining multiple filtersemail, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(60*time.Second), vaultsandbox.WithFrom("notifications@example.com"), vaultsandbox.WithSubjectRegex(regexp.MustCompile(`Order #\d+`)),)Waiting for Multiple Emails
Section titled “Waiting for Multiple Emails”// Wait until the inbox has at least 3 emailsemails, err := inbox.WaitForEmailCount(ctx, 3, vaultsandbox.WithWaitTimeout(60*time.Second),)if err != nil { log.Fatal(err)}
fmt.Printf("Received %d emails\n", len(emails))Getting Raw Email Content
Section titled “Getting Raw Email Content”// Get raw email (original MIME content)rawContent, err := inbox.GetRawEmail(ctx, email.ID)if err != nil { log.Fatal(err)}fmt.Println(rawContent)Marking Emails as Read
Section titled “Marking Emails as Read”err := inbox.MarkEmailAsRead(ctx, email.ID)if err != nil { log.Fatal(err)}Deleting Emails
Section titled “Deleting Emails”// Get the email first, then deleteemail, err := inbox.GetEmail(ctx, "email-id-123")if err != nil { log.Fatal(err)}err = inbox.DeleteEmail(ctx, email.ID)Deleting Inbox
Section titled “Deleting Inbox”// Delete inbox and all its emailserr := inbox.Delete(ctx)Inbox Isolation
Section titled “Inbox Isolation”Each inbox is completely isolated:
inbox1, _ := client.CreateInbox(ctx)inbox2, _ := client.CreateInbox(ctx)
// inbox1 cannot access inbox2's emails// inbox2 cannot access inbox1's emails
// Each has its own:// - Email address// - Encryption keys// - Email storage// - Expiration timeTime-to-Live (TTL)
Section titled “Time-to-Live (TTL)”Inboxes automatically expire after their TTL:
Default TTL
Section titled “Default TTL”// Uses default TTL (1 hour)inbox, err := client.CreateInbox(ctx)Custom TTL
Section titled “Custom TTL”// Expire after 1 hourinbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(time.Hour))
// Expire after 10 minutes (useful for quick tests)inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(10*time.Minute))
// Expire after 7 days (maximum allowed)inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(7*24*time.Hour))TTL Constraints
Section titled “TTL Constraints”const ( MinTTL = 60 * time.Second // Minimum TTL: 1 minute MaxTTL = 604800 * time.Second // Maximum TTL: 7 days)Checking Expiration
Section titled “Checking Expiration”minutesLeft := time.Until(inbox.ExpiresAt()).Minutes()
if minutesLeft < 5 { fmt.Println("Inbox expiring soon!")}Import and Export
Section titled “Import and Export”Inboxes can be exported and imported for:
- Test reproducibility
- Sharing between environments
- Backup and restore
Export
Section titled “Export”exportData := inbox.Export()
// Save to filejsonData, _ := json.MarshalIndent(exportData, "", " ")os.WriteFile("inbox.json", jsonData, 0600)
// Or use the convenience methoderr := client.ExportInboxToFile(inbox, "inbox.json")Import
Section titled “Import”// From ExportedInbox structjsonData, _ := os.ReadFile("inbox.json")var exportData vaultsandbox.ExportedInboxjson.Unmarshal(jsonData, &exportData)inbox, err := client.ImportInbox(ctx, &exportData)
// Or use the convenience methodinbox, err := client.ImportInboxFromFile(ctx, "inbox.json")
// Inbox restored with all encryption keysSecurity Warning: Exported data contains private keys. Treat as sensitive.
Watching for New Emails
Section titled “Watching for New Emails”Single Inbox Watching
Section titled “Single Inbox Watching”Use Watch() to receive new emails as they arrive via a channel:
ctx, cancel := context.WithCancel(context.Background())defer cancel()
// Watch returns a channel that receives new emailsfor email := range inbox.Watch(ctx) { fmt.Printf("New email: %s\n", email.Subject)}The channel is closed when the context is cancelled.
Multiple Inbox Watching
Section titled “Multiple Inbox Watching”Use WatchInboxes() to monitor multiple inboxes simultaneously:
ctx, cancel := context.WithCancel(context.Background())defer cancel()
// WatchInboxes returns a channel of InboxEventfor event := range client.WatchInboxes(ctx, inbox1, inbox2) { fmt.Printf("New email in %s: %s\n", event.Inbox.EmailAddress(), event.Email.Subject)}The InboxEvent struct contains:
type InboxEvent struct { Inbox *Inbox // The inbox that received the email Email *Email // The received email}Best Practices
Section titled “Best Practices”CI/CD Pipelines
Section titled “CI/CD Pipelines”Short TTL for fast cleanup:
inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(time.Hour))Always clean up:
inbox, err := client.CreateInbox(ctx)if err != nil { log.Fatal(err)}defer inbox.Delete(context.Background())
// Run testsTesting with Go
Section titled “Testing with Go”Test setup and teardown:
func TestPasswordReset(t *testing.T) { client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY")) if err != nil { t.Fatal(err) } defer client.Close()
ctx := context.Background() inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(2*time.Hour)) if err != nil { t.Fatal(err) } defer inbox.Delete(context.Background())
// Trigger password reset triggerPasswordReset(inbox.EmailAddress())
// Wait for email email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second), ) if err != nil { t.Fatal(err) }
// Assertions if email.Subject != "Password Reset" { t.Errorf("expected subject 'Password Reset', got %q", email.Subject) }}Manual Testing
Section titled “Manual Testing”Longer TTL for convenience:
inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(24*time.Hour))Export for reuse:
// Export after creatingerr := client.ExportInboxToFile(inbox, "test-inbox.json")
// Reuse in later sessionsinbox, err := client.ImportInboxFromFile(ctx, "test-inbox.json")Production Monitoring
Section titled “Production Monitoring”Monitor expiration:
go func() { ticker := time.NewTicker(time.Minute) defer ticker.Stop()
for range ticker.C { minutesLeft := time.Until(inbox.ExpiresAt()).Minutes() if minutesLeft < 10 { log.Printf("Inbox %s expiring in %.0f minutes", inbox.EmailAddress(), minutesLeft) } }}()Common Patterns
Section titled “Common Patterns”Dedicated Test Inbox
Section titled “Dedicated Test Inbox”var testInbox *vaultsandbox.Inboxvar testClient *vaultsandbox.Client
func TestMain(m *testing.M) { var err error testClient, err = vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY")) if err != nil { log.Fatal(err) }
testInbox, err = testClient.CreateInbox(context.Background(), vaultsandbox.WithTTL(2*time.Hour), ) if err != nil { log.Fatal(err) }
code := m.Run()
testInbox.Delete(context.Background()) testClient.Close()
os.Exit(code)}
func TestPasswordReset(t *testing.T) { ctx := context.Background() triggerPasswordReset(testInbox.EmailAddress())
email, err := testInbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second), ) if err != nil { t.Fatal(err) } // ...}Multiple Inboxes
Section titled “Multiple Inboxes”user1Inbox, _ := client.CreateInbox(ctx)user2Inbox, _ := client.CreateInbox(ctx)adminInbox, _ := client.CreateInbox(ctx)
// Each inbox receives emails independentlysendWelcomeEmail(user1Inbox.EmailAddress())sendWelcomeEmail(user2Inbox.EmailAddress())sendAdminReport(adminInbox.EmailAddress())Inbox Pool
Section titled “Inbox Pool”type InboxPool struct { client *vaultsandbox.Client pool chan *vaultsandbox.Inbox size int}
func NewInboxPool(client *vaultsandbox.Client, size int) *InboxPool { return &InboxPool{ client: client, pool: make(chan *vaultsandbox.Inbox, size), size: size, }}
func (p *InboxPool) Initialize(ctx context.Context) error { for i := 0; i < p.size; i++ { inbox, err := p.client.CreateInbox(ctx) if err != nil { return err } p.pool <- inbox } return nil}
func (p *InboxPool) Get() *vaultsandbox.Inbox { return <-p.pool}
func (p *InboxPool) Return(inbox *vaultsandbox.Inbox) { p.pool <- inbox}
func (p *InboxPool) Cleanup(ctx context.Context) { close(p.pool) for inbox := range p.pool { inbox.Delete(ctx) }}Error Handling
Section titled “Error Handling”Sentinel Errors
Section titled “Sentinel Errors”Use errors.Is() to check for specific error conditions:
import "errors"
emails, err := inbox.GetEmails(ctx)if err != nil { switch { case errors.Is(err, vaultsandbox.ErrInboxNotFound): log.Println("Inbox not found or expired") case errors.Is(err, vaultsandbox.ErrUnauthorized): log.Println("Invalid API key") case errors.Is(err, vaultsandbox.ErrRateLimited): log.Println("Rate limit exceeded, retry later") default: log.Printf("Unexpected error: %v", err) }}Available Sentinel Errors
Section titled “Available Sentinel Errors”| Error | Description |
|---|---|
ErrMissingAPIKey | No API key was provided to New() |
ErrClientClosed | Operation attempted on a closed client |
ErrUnauthorized | API key is invalid or expired (HTTP 401) |
ErrInboxNotFound | Inbox does not exist or has expired (HTTP 404) |
ErrEmailNotFound | Email does not exist (HTTP 404) |
ErrInboxAlreadyExists | Inbox with requested address already exists (HTTP 409) |
ErrInvalidImportData | Imported inbox data is malformed or invalid |
ErrDecryptionFailed | Email decryption failed |
ErrSignatureInvalid | Signature verification failed (potential tampering) |
ErrRateLimited | API rate limit exceeded (HTTP 429) |
Error Types
Section titled “Error Types”The SDK provides structured error types for detailed error handling:
// APIError - HTTP errors from the VaultSandbox APIvar apiErr *vaultsandbox.APIErrorif errors.As(err, &apiErr) { fmt.Printf("Status: %d\n", apiErr.StatusCode) fmt.Printf("Message: %s\n", apiErr.Message) fmt.Printf("Request ID: %s\n", apiErr.RequestID)}
// NetworkError - Network-level failuresvar netErr *vaultsandbox.NetworkErrorif errors.As(err, &netErr) { fmt.Printf("URL: %s\n", netErr.URL) fmt.Printf("Attempt: %d\n", netErr.Attempt) fmt.Printf("Underlying: %v\n", netErr.Err)}
// SignatureVerificationError - Signature verification failurevar sigErr *vaultsandbox.SignatureVerificationErrorif errors.As(err, &sigErr) { fmt.Printf("Message: %s\n", sigErr.Message) if sigErr.IsKeyMismatch { fmt.Println("Server key mismatch - potential key substitution attack") }}
// Timeouts - use context.DeadlineExceededif errors.Is(err, context.DeadlineExceeded) { fmt.Println("Operation timed out")}Error Type Definitions
Section titled “Error Type Definitions”type APIError struct { StatusCode int Message string RequestID string ResourceType ResourceType}
type NetworkError struct { Err error URL string Attempt int}
type SignatureVerificationError struct { Message string IsKeyMismatch bool // true if caused by server key mismatch}Troubleshooting
Section titled “Troubleshooting”Inbox Not Receiving Emails
Section titled “Inbox Not Receiving Emails”Check:
- Email is sent to correct address
- Inbox hasn’t expired
- DNS/MX records configured correctly
- SMTP connection successful
// Verify inbox still exists_, err := inbox.GetEmails(ctx) // Will error if inbox expiredif errors.Is(err, vaultsandbox.ErrInboxNotFound) { log.Println("Inbox has expired or was deleted")}Inbox Already Exists Error
Section titled “Inbox Already Exists Error”When requesting a specific email address:
inbox, err := client.CreateInbox(ctx, vaultsandbox.WithEmailAddress("test@mail.example.com"),)if errors.Is(err, vaultsandbox.ErrInboxAlreadyExists) { // Address already in use, generate random instead inbox, err = client.CreateInbox(ctx)}Inbox Expired
Section titled “Inbox Expired”emails, err := inbox.GetEmails(ctx)if errors.Is(err, vaultsandbox.ErrInboxNotFound) { log.Println("Inbox has expired") // Create new inbox newInbox, err := client.CreateInbox(ctx) if err != nil { log.Fatal(err) } // Continue with newInbox}Context Cancellation
Section titled “Context Cancellation”All inbox operations accept a context for cancellation and timeouts:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()
emails, err := inbox.GetEmails(ctx)if errors.Is(err, context.DeadlineExceeded) { log.Println("Operation timed out")}Client Management
Section titled “Client Management”Getting All Inboxes
Section titled “Getting All Inboxes”// Get all inboxes managed by this clientinboxes := client.Inboxes()for _, inbox := range inboxes { fmt.Printf("Inbox: %s (expires: %s)\n", inbox.EmailAddress(), inbox.ExpiresAt())}Getting a Specific Inbox
Section titled “Getting a Specific Inbox”inbox, exists := client.GetInbox("test@mail.example.com")if !exists { log.Println("Inbox not found in client")}Deleting a Specific Inbox
Section titled “Deleting a Specific Inbox”err := client.DeleteInbox(ctx, "test@mail.example.com")if err != nil { log.Fatal(err)}Deleting All Inboxes
Section titled “Deleting All Inboxes”count, err := client.DeleteAllInboxes(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Deleted %d inboxes\n", count)Validating API Key
Section titled “Validating API Key”err := client.CheckKey(ctx)if err != nil { log.Fatal("API key is invalid:", err)}fmt.Println("API key is valid")Getting Server Information
Section titled “Getting Server Information”info := client.ServerInfo()fmt.Printf("Allowed domains: %v\n", info.AllowedDomains)fmt.Printf("Max TTL: %v\n", info.MaxTTL)fmt.Printf("Default TTL: %v\n", info.DefaultTTL)fmt.Printf("Encryption policy: %s\n", info.EncryptionPolicy)The ServerInfo struct contains:
type ServerInfo struct { AllowedDomains []string // Email domains available for inbox creation MaxTTL time.Duration // Maximum allowed TTL for inboxes DefaultTTL time.Duration // Default TTL when not specified EncryptionPolicy EncryptionPolicy // Server's encryption policy}Encryption Policy
Section titled “Encryption Policy”The EncryptionPolicy field indicates how the server handles inbox encryption:
type EncryptionPolicy string
const ( EncryptionPolicyAlways EncryptionPolicy = "always" // All inboxes encrypted, no override EncryptionPolicyEnabled EncryptionPolicy = "enabled" // Encrypted by default, can request plain EncryptionPolicyDisabled EncryptionPolicy = "disabled" // Plain by default, can request encrypted EncryptionPolicyNever EncryptionPolicy = "never" // All inboxes plain, no override)| Policy | Default Encryption | Per-Inbox Override |
|---|---|---|
always | Encrypted | No - all inboxes encrypted |
enabled | Encrypted | Yes - can request plain |
disabled | Plain | Yes - can request encrypted |
never | Plain | No - all inboxes plain |
Helper Methods:
// Check if we can override encryption settings per-inboxif info.EncryptionPolicy.CanOverride() { // Can use WithEncryption() option inbox, err := client.CreateInbox(ctx, vaultsandbox.WithEncryption(vaultsandbox.EncryptionModePlain))}
// Check default encryption stateif info.EncryptionPolicy.DefaultEncrypted() { fmt.Println("Inboxes are encrypted by default")}Next Steps
Section titled “Next Steps”- Email Objects - Learn about email structure
- Managing Inboxes - Common inbox operations
- Import/Export - Advanced inbox persistence
- API Reference: Inbox - Complete API documentation