Skip to content

Client Configuration

This page covers all configuration options for the VaultSandbox Go client.

import "github.com/vaultsandbox/client-go"
client, err := vaultsandbox.New("your-api-key",
vaultsandbox.WithBaseURL("https://mail.example.com"),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()

Type: string

Description: API key for authentication. Passed as the first argument to New().

Example:

client, err := vaultsandbox.New("vs_1234567890abcdef...")

Best practices:

  • Store in environment variables
  • Never commit to version control
  • Rotate periodically

Options are passed as variadic arguments to New().

Signature: WithBaseURL(url string) Option

Default: https://api.vaultsandbox.com

Description: Base URL of your VaultSandbox Gateway

Examples:

vaultsandbox.WithBaseURL("https://mail.example.com")
vaultsandbox.WithBaseURL("http://localhost:3000") // Local development

Requirements:

  • Must include protocol (https:// or http://)
  • Should not include trailing slash
  • Must be accessible from your application

Signature: WithDeliveryStrategy(strategy DeliveryStrategy) Option

Default: StrategySSE

Description: Email delivery strategy

Options:

  • StrategySSE - Server-Sent Events for real-time delivery (default)
  • StrategyPolling - Poll for new emails at intervals

Examples:

vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategySSE) // Default, can be omitted
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling) // Use polling

When to use each:

  • StrategySSE: Most use cases - provides real-time, low-latency delivery (default)
  • StrategyPolling: When SSE is blocked by firewall/proxy or in CI environments

Signature: WithTimeout(timeout time.Duration) Option

Default: 60 * time.Second

Description: Default timeout for wait operations (WaitForEmail, WaitForEmailCount) and API key validation during client creation. This timeout is also passed to the internal HTTP client for individual requests.

Note: This controls the maximum time for wait operations, not per-HTTP-request timeouts. For fine-grained HTTP timeout control, use WithHTTPClient with a custom http.Client.

Examples:

vaultsandbox.WithTimeout(30 * time.Second)
vaultsandbox.WithTimeout(2 * time.Minute)

Signature: WithRetries(count int) Option

Default: 3

Description: Maximum retry attempts for failed HTTP requests

Examples:

vaultsandbox.WithRetries(3) // Default
vaultsandbox.WithRetries(5) // More resilient
vaultsandbox.WithRetries(0) // No retries

Signature: WithRetryOn(statusCodes []int) Option

Default: []int{408, 429, 500, 502, 503, 504}

Description: HTTP status codes that trigger a retry

Example:

vaultsandbox.WithRetryOn([]int{408, 429, 500, 502, 503, 504}) // Default
vaultsandbox.WithRetryOn([]int{500, 502, 503}) // Only server errors
vaultsandbox.WithRetryOn([]int{}) // Never retry

Signature: WithOnSyncError(fn func(error)) Option

Default: nil (errors are silently ignored)

Description: Callback invoked when background sync fails to fetch emails after an SSE reconnection. Use this to log or handle errors that would otherwise be silently dropped.

Example:

client, err := vaultsandbox.New(apiKey,
vaultsandbox.WithOnSyncError(func(err error) {
log.Printf("background sync error: %v", err)
}),
)

Signature: WithHTTPClient(client *http.Client) Option

Default: http.DefaultClient with timeout

Description: Custom HTTP client for advanced networking needs

Example:

httpClient := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
},
}
client, err := vaultsandbox.New(apiKey,
vaultsandbox.WithHTTPClient(httpClient),
)

Signature: WithPollingConfig(cfg PollingConfig) Option

Description: Sets all polling-related options at once. This is the recommended way to customize polling behavior when you need to change multiple settings. Zero values are ignored, so you only need to specify the fields you want to change.

PollingConfig struct:

type PollingConfig struct {
InitialInterval time.Duration // Default: 2 * time.Second
MaxBackoff time.Duration // Default: 30 * time.Second
BackoffMultiplier float64 // Default: 1.5
JitterFactor float64 // Default: 0.3 (30%)
}

Example:

client, err := vaultsandbox.New(apiKey,
vaultsandbox.WithPollingConfig(vaultsandbox.PollingConfig{
InitialInterval: 1 * time.Second, // Faster initial polling
MaxBackoff: 10 * time.Second, // Lower max backoff
}),
)

When to use: Use WithPollingConfig when you need to customize polling settings. Zero values are ignored, so you only need to specify the fields you want to change.

Options passed to CreateInbox().

Signature: WithTTL(ttl time.Duration) InboxOption

Default: 1 * time.Hour

Description: Time-to-live for the inbox

Constraints:

  • Minimum: 1 minute (MinTTL)
  • Maximum: 7 days (MaxTTL) or server-configured limit

Examples:

inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithTTL(30 * time.Minute),
)
inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithTTL(24 * time.Hour),
)

Signature: WithEmailAddress(email string) InboxOption

Default: Auto-generated

Description: Request a specific email address

Example:

inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithEmailAddress("test-user@mail.example.com"),
)

Signature: WithEmailAuth(enabled bool) InboxOption

Default: Server default

Description: Enable or disable email authentication (SPF/DKIM/DMARC) validation for this inbox

Example:

inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithEmailAuth(true),
)

Signature: WithEncryption(mode EncryptionMode) InboxOption

Default: EncryptionModeDefault

Description: Set the encryption mode for the inbox

Options:

  • EncryptionModeDefault - Use server default
  • EncryptionModeEncrypted - Request encrypted inbox
  • EncryptionModePlain - Request unencrypted inbox

Example:

inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithEncryption(vaultsandbox.EncryptionModeEncrypted),
)

Signature: WithSpamAnalysis(enabled bool) InboxOption

Default: Server default (VSB_SPAM_ANALYSIS_INBOX_DEFAULT)

Description: Enable or disable spam analysis (Rspamd integration) for this inbox. When enabled, incoming emails are analyzed for spam and results are included in the SpamAnalysis field of the email.

Requirements: The server must have spam analysis enabled (VSB_SPAM_ANALYSIS_ENABLED=true). Check client.ServerInfo().SpamAnalysisEnabled to verify availability.

Example:

// Enable spam analysis for this inbox
inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithSpamAnalysis(true),
)
// Disable spam analysis for this inbox
inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithSpamAnalysis(false),
)
// Check if server supports spam analysis
if client.ServerInfo().SpamAnalysisEnabled {
inbox, err := client.CreateInbox(ctx, vaultsandbox.WithSpamAnalysis(true))
}

Options passed to WaitForEmail() and WaitForEmailCount().

Signature: WithWaitTimeout(timeout time.Duration) WaitOption

Default: 60 * time.Second

Description: Maximum time to wait for email

Example:

email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithWaitTimeout(30 * time.Second),
)

Signature: WithSubject(subject string) WaitOption

Description: Filter emails by exact subject match

Example:

email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithSubject("Password Reset"),
)

Signature: WithSubjectRegex(pattern *regexp.Regexp) WaitOption

Description: Filter emails by subject regex

Example:

pattern := regexp.MustCompile(`(?i)password.*reset`)
email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithSubjectRegex(pattern),
)

Signature: WithFrom(from string) WaitOption

Description: Filter emails by exact sender match

Example:

email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithFrom("noreply@example.com"),
)

Signature: WithFromRegex(pattern *regexp.Regexp) WaitOption

Description: Filter emails by sender regex

Example:

pattern := regexp.MustCompile(`@example\.com$`)
email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithFromRegex(pattern),
)

Signature: WithPredicate(fn func(*Email) bool) WaitOption

Description: Filter emails by custom predicate function

Example:

email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithPredicate(func(e *vaultsandbox.Email) bool {
return len(e.Attachments) > 0
}),
)
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
// SSE is the default strategy, no need to specify
vaultsandbox.WithRetries(5),
vaultsandbox.WithTimeout(60 * time.Second),
)
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling), // Use polling for CI compatibility
vaultsandbox.WithRetries(3),
vaultsandbox.WithTimeout(30 * time.Second),
)
client, err := vaultsandbox.New("dev-api-key",
vaultsandbox.WithBaseURL("http://localhost:3000"),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling),
vaultsandbox.WithRetries(1),
)
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
vaultsandbox.WithRetries(10),
vaultsandbox.WithRetryOn([]int{408, 429, 500, 502, 503, 504}),
vaultsandbox.WithTimeout(2 * time.Minute),
)
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling),
vaultsandbox.WithPollingConfig(vaultsandbox.PollingConfig{
InitialInterval: 1 * time.Second, // Faster initial polling
MaxBackoff: 15 * time.Second, // Lower max backoff
BackoffMultiplier: 1.2, // Slower backoff growth
JitterFactor: 0.2, // Less jitter
}),
)
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling),
vaultsandbox.WithPollingConfig(vaultsandbox.PollingConfig{
InitialInterval: 1 * time.Second, // Start with faster polling
MaxBackoff: 20 * time.Second, // Lower max backoff
}),
)

Store configuration in environment variables:

Terminal window
VAULTSANDBOX_URL=https://mail.example.com
VAULTSANDBOX_API_KEY=vs_1234567890abcdef...
import (
"os"
"github.com/joho/godotenv"
"github.com/vaultsandbox/client-go"
)
func main() {
// Load .env file
godotenv.Load()
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
}

Create a new temporary email inbox:

inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithTTL(30 * time.Minute),
vaultsandbox.WithEmailAddress("test@mail.example.com"),
)

Signature: CreateInbox(ctx context.Context, opts ...InboxOption) (*Inbox, error)

Returns: A new *Inbox that can receive emails.

Import a previously exported inbox:

inbox, err := client.ImportInbox(ctx, exportedData)

Signature: ImportInbox(ctx context.Context, data *ExportedInbox) (*Inbox, error)

Returns: The restored *Inbox.

Errors: Returns ErrInboxAlreadyExists if the inbox is already managed by this client.

Delete an inbox by email address:

err := client.DeleteInbox(ctx, "test@mail.example.com")

Signature: DeleteInbox(ctx context.Context, emailAddress string) error

Delete all inboxes managed by this client:

count, err := client.DeleteAllInboxes(ctx)
fmt.Printf("Deleted %d inboxes\n", count)

Signature: DeleteAllInboxes(ctx context.Context) (int, error)

Returns: The number of inboxes deleted.

Get an inbox by email address:

inbox, exists := client.GetInbox("test@mail.example.com")
if exists {
// Use inbox
}

Signature: GetInbox(emailAddress string) (*Inbox, bool)

Returns: The inbox and true if found, otherwise nil and false.

Get all inboxes managed by this client:

inboxes := client.Inboxes()
for _, inbox := range inboxes {
fmt.Println(inbox.EmailAddress())
}

Signature: Inboxes() []*Inbox

Get server configuration:

info := client.ServerInfo()
fmt.Printf("Max TTL: %v\n", info.MaxTTL)
fmt.Printf("Allowed domains: %v\n", info.AllowedDomains)

Signature: ServerInfo() *ServerInfo

Returns: *ServerInfo containing:

  • AllowedDomains []string - Email domains the server accepts
  • MaxTTL time.Duration - Maximum inbox TTL allowed
  • DefaultTTL time.Duration - Default inbox TTL
  • EncryptionPolicy EncryptionPolicy - Server encryption policy
  • SpamAnalysisEnabled bool - Whether spam analysis is available

Validate the API key:

err := client.CheckKey(ctx)
if err != nil {
log.Fatal("Invalid API key")
}

Signature: CheckKey(ctx context.Context) error

Note: The API key is automatically validated when creating a client with New().

Export an inbox to a JSON file:

err := client.ExportInboxToFile(inbox, "/path/to/inbox.json")

Signature: ExportInboxToFile(inbox *Inbox, filePath string) error

Warning: The exported file contains private key material. Handle securely.

Import an inbox from a JSON file:

inbox, err := client.ImportInboxFromFile(ctx, "/path/to/inbox.json")

Signature: ImportInboxFromFile(ctx context.Context, filePath string) (*Inbox, error)

Watch multiple inboxes for new emails via a channel:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for event := range client.WatchInboxes(ctx, inbox1, inbox2) {
fmt.Printf("New email in %s: %s\n", event.Inbox.EmailAddress(), event.Email.Subject)
}

Signature: WatchInboxes(ctx context.Context, inboxes ...*Inbox) <-chan *InboxEvent

Returns: A receive-only channel of *InboxEvent that closes when context is cancelled.

Close the client and clean up resources:

err := client.Close()

What it does:

  • Terminates all active SSE connections
  • Stops all polling operations
  • Cleans up resources

When to use:

  • After test suite completes
  • Before process exit
  • When client is no longer needed

Example:

client, err := vaultsandbox.New(apiKey, vaultsandbox.WithBaseURL(url))
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Use client
inbox, err := client.CreateInbox(ctx)
// ...

Get the inbox email address:

email := inbox.EmailAddress()

Signature: EmailAddress() string

Get when the inbox expires:

expiry := inbox.ExpiresAt()
fmt.Printf("Expires at: %v\n", expiry)

Signature: ExpiresAt() time.Time

Get the SHA-256 hash of the public key:

hash := inbox.InboxHash()

Signature: InboxHash() string

Check if the inbox has expired:

if inbox.IsExpired() {
// Create a new inbox
}

Signature: IsExpired() bool

Get the synchronization status of the inbox:

status, err := inbox.GetSyncStatus(ctx)
fmt.Printf("Email count: %d\n", status.EmailCount)
fmt.Printf("Emails hash: %s\n", status.EmailsHash)

Signature: GetSyncStatus(ctx context.Context) (*SyncStatus, error)

Returns: *SyncStatus containing:

  • EmailCount int - Number of emails in the inbox
  • EmailsHash string - Hash of the email list for change detection

Fetch all emails in the inbox:

emails, err := inbox.GetEmails(ctx)
for _, email := range emails {
fmt.Printf("Subject: %s\n", email.Subject)
}

Signature: GetEmails(ctx context.Context) ([]*Email, error)

Fetch a specific email by ID:

email, err := inbox.GetEmail(ctx, "email-id-123")

Signature: GetEmail(ctx context.Context, emailID string) (*Email, error)

Wait for an email matching the given criteria:

email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithSubject("Welcome"),
vaultsandbox.WithWaitTimeout(30 * time.Second),
)

Signature: WaitForEmail(ctx context.Context, opts ...WaitOption) (*Email, error)

Wait until the inbox has at least N emails matching criteria:

emails, err := inbox.WaitForEmailCount(ctx, 3,
vaultsandbox.WithFrom("noreply@example.com"),
)

Signature: WaitForEmailCount(ctx context.Context, count int, opts ...WaitOption) ([]*Email, error)

Delete the inbox:

err := inbox.Delete(ctx)

Signature: Delete(ctx context.Context) error

Export inbox data including private key:

data := inbox.Export()
// data can be serialized to JSON and stored

Signature: Export() *ExportedInbox

Warning: The returned data contains private key material. Handle securely.

Watch for new email notifications via a channel:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for email := range inbox.Watch(ctx) {
fmt.Printf("New email: %s\n", email.Subject)
}

Signature: Watch(ctx context.Context) <-chan *Email

Returns: A receive-only channel of *Email that closes when context is cancelled.

Email is a pure data struct with no methods. Use Inbox methods to perform operations on emails.

Fetch the raw email content (RFC 5322 format):

raw, err := inbox.GetRawEmail(ctx, email.ID)
fmt.Println(raw)

Signature: GetRawEmail(ctx context.Context, emailID string) (string, error)

Mark an email as read:

err := inbox.MarkEmailAsRead(ctx, email.ID)

Signature: MarkEmailAsRead(ctx context.Context, emailID string) error

Delete an email:

err := inbox.DeleteEmail(ctx, email.ID)

Signature: DeleteEmail(ctx context.Context, emailID string) error

Represents a decrypted email:

type Email struct {
ID string
From string
To []string
Subject string
Text string
HTML string
ReceivedAt time.Time
Headers map[string]string
Attachments []Attachment
Links []string
AuthResults *authresults.AuthResults
SpamAnalysis *spamanalysis.SpamAnalysis
IsRead bool
}

Represents an email attachment:

type Attachment struct {
Filename string
ContentType string
Size int
ContentID string
ContentDisposition string
Content []byte
Checksum string
}

Contains server configuration:

type ServerInfo struct {
AllowedDomains []string
MaxTTL time.Duration
DefaultTTL time.Duration
EncryptionPolicy EncryptionPolicy
SpamAnalysisEnabled bool
}

Represents the synchronization status of an inbox:

type SyncStatus struct {
EmailCount int
EmailsHash string
}

Contains all data needed to restore an inbox:

type ExportedInbox struct {
Version int
EmailAddress string
ExpiresAt time.Time
InboxHash string
ServerSigPk string // Only for encrypted inboxes
SecretKey string // Only for encrypted inboxes
ExportedAt time.Time
EmailAuth bool
Encrypted bool
}

Warning: Contains private key material. Handle securely.

Event struct returned when watching multiple inboxes:

type InboxEvent struct {
Inbox *Inbox // The inbox that received the email
Email *Email // The received email
}

Use when: Most use cases - provides real-time, low-latency delivery

Behavior:

  • Persistent connection to server
  • Push-based email notification
  • Near-instant delivery

Pros:

  • Real-time delivery (no polling delay)
  • Efficient (no repeated HTTP requests)
  • Deterministic tests
  • Lower server load

Cons:

  • Requires persistent connection support
  • May be blocked by some corporate proxies/firewalls

Use when: SSE is blocked or unreliable (e.g., some CI environments)

Behavior:

  • Periodic HTTP requests for new emails
  • Pull-based email retrieval
  • Adaptive backoff intervals

Pros:

  • Works in all network environments
  • No persistent connection required
  • Simple and predictable
  • Guaranteed compatibility

Cons:

  • Delay based on polling interval
  • More HTTP requests
  • Less efficient than SSE

Do:

// Use environment variables
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
)

Don’t:

// Hard-code credentials
client, err := vaultsandbox.New("vs_1234567890...", // Never do this
vaultsandbox.WithBaseURL("https://mail.example.com"),
)

Do:

client, err := vaultsandbox.New(apiKey, vaultsandbox.WithBaseURL(url))
if err != nil {
log.Fatal(err)
}
defer client.Close() // Always clean up
runTests()

Don’t:

client, err := vaultsandbox.New(apiKey, vaultsandbox.WithBaseURL(url))
runTests()
// Forgot to close, resources leak

Error Types:

  • APIError - HTTP errors from the API with StatusCode, Message, RequestID, and ResourceType fields
  • NetworkError - Network-level failures with Err (underlying error), URL, and Attempt fields
  • SignatureVerificationError - Signature verification failures with Message and IsKeyMismatch fields

Sentinel Errors (use with errors.Is()):

  • ErrMissingAPIKey - No API key provided to New()
  • ErrClientClosed - Client has been closed
  • ErrUnauthorized - Invalid or expired API key
  • ErrInboxNotFound - Inbox does not exist or has expired
  • ErrEmailNotFound - Email does not exist
  • ErrInboxAlreadyExists - Inbox already exists (when importing duplicate)
  • ErrInvalidImportData - Invalid inbox import data
  • ErrDecryptionFailed - Email decryption failed
  • ErrSignatureInvalid - Signature verification failed
  • ErrRateLimited - API rate limit exceeded

Timeouts: Use context.DeadlineExceeded for timeout handling

Do:

inbox, err := client.CreateInbox(ctx)
if err != nil {
var apiErr *vaultsandbox.APIError
var netErr *vaultsandbox.NetworkError
switch {
case errors.As(err, &apiErr):
log.Printf("API error %d: %s (request: %s)", apiErr.StatusCode, apiErr.Message, apiErr.RequestID)
case errors.As(err, &netErr):
log.Printf("Network error on %s (attempt %d): %v", netErr.URL, netErr.Attempt, netErr.Err)
case errors.Is(err, vaultsandbox.ErrUnauthorized):
log.Printf("Invalid API key")
case errors.Is(err, vaultsandbox.ErrRateLimited):
log.Printf("Rate limited, retry later")
default:
log.Printf("Unexpected error: %v", err)
}
return
}

Do:

// Use context for cancellation and timeouts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
email, err := inbox.WaitForEmail(ctx)

Don’t:

// Don't use context.Background() for long operations without timeout
email, err := inbox.WaitForEmail(context.Background()) // May hang forever