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: StrategyAuto

Description: Email delivery strategy

Options:

  • StrategyAuto - Automatically choose best strategy (tries SSE first, falls back to polling)
  • StrategySSE - Server-Sent Events for real-time delivery
  • StrategyPolling - Poll for new emails at intervals

Examples:

vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyAuto) // Recommended
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategySSE) // Force SSE
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyPolling) // Force polling

When to use each:

  • StrategyAuto: Most use cases (recommended)
  • StrategySSE: When you need real-time, low-latency delivery
  • StrategyPolling: When SSE is blocked by firewall/proxy

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%)
SSEConnectionTimeout time.Duration // Default: 5 * time.Second
}

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("[email protected]"),
)

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("[email protected]"),
)

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")),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyAuto),
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.StrategyAuto),
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.StrategyAuto),
vaultsandbox.WithPollingConfig(vaultsandbox.PollingConfig{
SSEConnectionTimeout: 10 * time.Second, // More time for SSE
InitialInterval: 2 * time.Second, // Fallback polling config
MaxBackoff: 30 * time.Second,
}),
)

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("[email protected]"),
)

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, "[email protected]")

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("[email protected]")
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

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("[email protected]"),
)

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
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
}

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 {
EmailAddress string
ExpiresAt time.Time
InboxHash string
ServerSigPk string
PublicKeyB64 string
SecretKeyB64 string
ExportedAt time.Time
}

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: You want optimal performance with automatic fallback

Behavior:

  1. Tries SSE first
  2. Falls back to polling if SSE fails
  3. Automatically reconnects on errors

Pros:

  • Best of both worlds
  • No manual configuration needed
  • Resilient to network issues

Cons:

  • Slightly more complex internally

Use when: You need real-time, low-latency delivery

Behavior:

  • Persistent connection to server
  • Push-based email notification
  • Instant delivery

Pros:

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

Cons:

  • Requires persistent connection
  • May be blocked by some proxies/firewalls
  • More complex error handling

Use when: SSE is blocked or unreliable

Behavior:

  • Periodic HTTP requests for new emails
  • Pull-based email retrieval
  • Configurable interval

Pros:

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

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