Skip to content

Client API

The Client is the main entry point for interacting with the VaultSandbox Gateway. It handles authentication, inbox creation, and provides utility methods for managing inboxes.

TTL (time-to-live) constants for inbox creation:

const (
MinTTL = 60 * time.Second // Minimum TTL: 1 minute
MaxTTL = 604800 * time.Second // Maximum TTL: 7 days
)
func New(apiKey string, opts ...Option) (*Client, error)

Creates a new VaultSandbox client instance.

Configuration options for the client using the functional options pattern.

// Available options
WithBaseURL(url string) Option
WithHTTPClient(client *http.Client) Option
WithDeliveryStrategy(strategy DeliveryStrategy) Option
WithTimeout(timeout time.Duration) Option
WithRetries(count int) Option
WithRetryOn(statusCodes []int) Option
WithOnSyncError(fn func(error)) Option
OptionTypeDefaultDescription
WithBaseURLstringhttps://api.vaultsandbox.comGateway URL
WithHTTPClient*http.ClientDefault clientCustom HTTP client
WithDeliveryStrategyDeliveryStrategyStrategyAutoEmail delivery strategy
WithTimeouttime.Duration60sRequest timeout
WithRetriesint3Maximum retry attempts for HTTP requests
WithRetryOn[]int[408, 429, 500, 502, 503, 504]HTTP status codes that trigger a retry
WithOnSyncErrorfunc(error)nilCallback for background sync errors

For advanced control over polling behavior, use WithPollingConfig:

// Polling configuration struct
type PollingConfig struct {
InitialInterval time.Duration // Starting polling interval (default: 2s)
MaxBackoff time.Duration // Maximum polling interval (default: 30s)
BackoffMultiplier float64 // Interval multiplier after no changes (default: 1.5)
JitterFactor float64 // Randomness factor to prevent synchronized polling (default: 0.3)
SSEConnectionTimeout time.Duration // Timeout for SSE before falling back to polling (default: 5s)
}
WithPollingConfig(cfg PollingConfig) Option
FieldTypeDefaultDescription
InitialIntervaltime.Duration2sStarting polling interval
MaxBackofftime.Duration30sMaximum polling interval after backoff
BackoffMultiplierfloat641.5Multiplier for interval after each poll with no changes
JitterFactorfloat640.3Random jitter factor (30%) to prevent synchronized polling
SSEConnectionTimeouttime.Duration5sTimeout for SSE connection in auto mode before fallback
const (
StrategyAuto DeliveryStrategy = "auto" // SSE with fallback to polling
StrategySSE DeliveryStrategy = "sse" // Server-Sent Events only
StrategyPolling DeliveryStrategy = "polling" // Periodic polling only
)
package main
import (
"os"
"time"
vaultsandbox "github.com/vaultsandbox/client-go"
)
func main() {
client, err := vaultsandbox.New(
os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL("https://api.vaultsandbox.com"),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyAuto),
vaultsandbox.WithRetries(5),
vaultsandbox.WithTimeout(30*time.Second),
)
if err != nil {
panic(err)
}
defer client.Close()
}

Creates a new email inbox with automatic key generation and encryption setup.

func (c *Client) CreateInbox(ctx context.Context, opts ...InboxOption) (*Inbox, error)
  • ctx: Context for cancellation and timeouts
  • opts (optional): Configuration options for the inbox
// Available inbox options
WithTTL(ttl time.Duration) InboxOption
WithEmailAddress(email string) InboxOption
OptionTypeDescription
WithTTLtime.DurationTime-to-live for the inbox (min: 60s, max: 7 days, default: 1 hour)
WithEmailAddressstringRequest a specific email address (e.g., [email protected])
  • *Inbox - The created inbox instance
  • error - Any error that occurred
ctx := context.Background()
// Create inbox with default settings
inbox, err := client.CreateInbox(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(inbox.EmailAddress())
// Create inbox with custom TTL (1 hour)
inbox, err := client.CreateInbox(ctx, vaultsandbox.WithTTL(time.Hour))
// Request specific email address
inbox, err := client.CreateInbox(ctx,
vaultsandbox.WithEmailAddress("[email protected]"),
)
  • ErrUnauthorized - Invalid API key
  • ErrInboxAlreadyExists - Requested email address is already in use
  • *NetworkError - Network connection failure
  • *APIError - API-level error (invalid request, permission denied)

Deletes all inboxes associated with the current API key. Useful for cleanup in test environments.

func (c *Client) DeleteAllInboxes(ctx context.Context) (int, error)
  • int - Number of inboxes deleted
  • error - Any error that occurred
deleted, err := client.DeleteAllInboxes(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted %d inboxes\n", deleted)

Use this in test cleanup to avoid orphaned inboxes:

func TestMain(m *testing.M) {
// Setup
client, _ := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"))
code := m.Run()
// Cleanup
deleted, _ := client.DeleteAllInboxes(context.Background())
if deleted > 0 {
log.Printf("Cleaned up %d orphaned inboxes\n", deleted)
}
client.Close()
os.Exit(code)
}

Returns information about the VaultSandbox Gateway server. This information is fetched once during client initialization.

func (c *Client) ServerInfo() *ServerInfo

*ServerInfo - Server information struct

type ServerInfo struct {
AllowedDomains []string
MaxTTL time.Duration
DefaultTTL time.Duration
}
FieldTypeDescription
AllowedDomains[]stringList of domains allowed for inbox creation
MaxTTLtime.DurationMaximum time-to-live for inboxes
DefaultTTLtime.DurationDefault time-to-live for inboxes
info := client.ServerInfo()
fmt.Printf("Max TTL: %v, Default TTL: %v\n", info.MaxTTL, info.DefaultTTL)
fmt.Printf("Allowed domains: %v\n", info.AllowedDomains)

Validates the API key with the server.

func (c *Client) CheckKey(ctx context.Context) error
  • error - nil if the API key is valid, otherwise an error
if err := client.CheckKey(ctx); err != nil {
log.Fatal("Invalid API key:", err)
}

Useful for verifying configuration before running tests:

func TestMain(m *testing.M) {
client, err := vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"))
if err != nil {
log.Fatal(err)
}
if err := client.CheckKey(context.Background()); err != nil {
log.Fatal("VaultSandbox API key is invalid:", err)
}
os.Exit(m.Run())
}

Returns a channel that receives events from multiple inboxes. The channel closes when the context is cancelled.

func (c *Client) WatchInboxes(ctx context.Context, inboxes ...*Inbox) <-chan *InboxEvent
  • ctx: Context for cancellation - when cancelled, the channel closes and all watchers are cleaned up
  • inboxes: Variadic list of inbox instances to watch
  • <-chan *InboxEvent - Receive-only channel of inbox events
type InboxEvent struct {
Inbox *Inbox // The inbox that received the email
Email *Email // The received email
}
inbox1, _ := client.CreateInbox(ctx)
inbox2, _ := client.CreateInbox(ctx)
watchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
for event := range client.WatchInboxes(watchCtx, inbox1, inbox2) {
fmt.Printf("New email in %s: %s\n", event.Inbox.EmailAddress(), event.Email.Subject)
}
  • Returns immediately closed channel if no inboxes provided
  • Channel has buffer size of 16
  • Non-blocking sends: if channel buffer is full, events may be dropped
  • All internal goroutines and watchers are cleaned up when context is cancelled

See Real-time Monitoring Guide for more details.


Calls a callback function for each event from multiple inboxes until the context is cancelled. This is a convenience wrapper around WatchInboxes for simpler use cases where you don’t need channel semantics.

func (c *Client) WatchInboxesFunc(ctx context.Context, fn func(*InboxEvent), inboxes ...*Inbox)
  • ctx: Context for cancellation - when cancelled, watching stops
  • fn: Callback function called for each new event
  • inboxes: Variadic list of inbox instances to watch
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
client.WatchInboxesFunc(ctx, func(event *vaultsandbox.InboxEvent) {
fmt.Printf("Email in %s: %s\n", event.Inbox.EmailAddress(), event.Email.Subject)
// Route based on inbox
switch event.Inbox.EmailAddress() {
handleAlert(event.Email)
default:
handleGeneral(event.Email)
}
}, inbox1, inbox2, inbox3)
  • Blocks until context is cancelled
  • Each event is passed to the callback function
  • Uses WatchInboxes internally with proper context handling

Use WatchInboxesFunc instead of WatchInboxes when:

  • You prefer callback-style processing over channel iteration
  • You want simpler code without channel select statements
  • You’re processing events in a blocking manner

Retrieves an inbox by its email address from the client’s managed inboxes.

func (c *Client) GetInbox(emailAddress string) (*Inbox, bool)
  • emailAddress: The email address of the inbox to retrieve
  • *Inbox - The inbox instance if found
  • bool - true if the inbox was found, false otherwise
inbox, ok := client.GetInbox("[email protected]")
if !ok {
log.Fatal("Inbox not found")
}
fmt.Println(inbox.EmailAddress())

Returns all inboxes currently managed by the client.

func (c *Client) Inboxes() []*Inbox

[]*Inbox - Slice of all managed inbox instances

for _, inbox := range client.Inboxes() {
fmt.Printf("Inbox: %s (expires: %v)\n", inbox.EmailAddress(), inbox.ExpiresAt())
}

Deletes a specific inbox by its email address.

func (c *Client) DeleteInbox(ctx context.Context, emailAddress string) error
  • ctx: Context for cancellation and timeouts
  • emailAddress: The email address of the inbox to delete
  • error - Any error that occurred
err := client.DeleteInbox(ctx, "[email protected]")
if err != nil {
log.Fatal(err)
}

Exports an inbox to a JSON file on disk. The exported data includes sensitive key material and should be treated as confidential.

func (c *Client) ExportInboxToFile(inbox *Inbox, filePath string) error
  • inbox: Inbox instance to export
  • filePath: Path where the JSON file will be written
  • error - Any error that occurred
inbox, _ := client.CreateInbox(ctx)
// Export to file
err := client.ExportInboxToFile(inbox, "./backup/inbox.json")
if err != nil {
log.Fatal(err)
}
fmt.Println("Inbox exported to ./backup/inbox.json")

Exported data contains private encryption keys. Store securely and never commit to version control.


Imports a previously exported inbox, restoring all data and encryption keys.

func (c *Client) ImportInbox(ctx context.Context, data *ExportedInbox) (*Inbox, error)
  • ctx: Context for cancellation and timeouts
  • data: Previously exported inbox data
  • *Inbox - The imported inbox instance
  • error - Any error that occurred
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"`
}
// Load exported data
data, _ := os.ReadFile("./backup/inbox.json")
var exportedData vaultsandbox.ExportedInbox
json.Unmarshal(data, &exportedData)
inbox, err := client.ImportInbox(ctx, &exportedData)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Imported inbox: %s\n", inbox.EmailAddress())
// Use inbox normally
emails, _ := inbox.GetEmails(ctx)
  • ErrInboxAlreadyExists - Inbox is already imported in this client
  • ErrInvalidImportData - Import data is invalid or corrupted
  • *APIError - Server rejected the import (inbox may not exist)

Imports an inbox from a JSON file.

func (c *Client) ImportInboxFromFile(ctx context.Context, filePath string) (*Inbox, error)
  • ctx: Context for cancellation and timeouts
  • filePath: Path to the exported inbox JSON file
  • *Inbox - The imported inbox instance
  • error - Any error that occurred
// Import from file
inbox, err := client.ImportInboxFromFile(ctx, "./backup/inbox.json")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Imported inbox: %s\n", inbox.EmailAddress())
// Watch for new emails
watchCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
for email := range inbox.Watch(watchCtx) {
fmt.Printf("New email: %s\n", email.Subject)
}
  • Test reproducibility across runs
  • Sharing inboxes between environments
  • Manual testing workflows
  • Debugging production issues

Closes the client, terminates any active SSE or polling connections, and cleans up resources.

func (c *Client) Close() error
  • error - Any error that occurred during cleanup
client, _ := vaultsandbox.New(apiKey)
defer client.Close()
inbox, _ := client.CreateInbox(ctx)
// Use inbox...

Always close the client when done, especially in long-running processes:

var client *vaultsandbox.Client
func TestMain(m *testing.M) {
var err error
client, err = vaultsandbox.New(os.Getenv("VAULTSANDBOX_API_KEY"))
if err != nil {
log.Fatal(err)
}
code := m.Run()
client.Close()
os.Exit(code)
}

The client package exports sentinel errors for use with errors.Is() checks, as well as error types for detailed error handling.

var (
ErrMissingAPIKey error // No API key provided
ErrClientClosed error // Operations attempted on a closed client
ErrUnauthorized error // Invalid or expired API key
ErrInboxNotFound error // Inbox not found
ErrEmailNotFound error // Email not found
ErrInboxAlreadyExists error // Inbox already exists (import conflict)
ErrInvalidImportData error // Invalid or corrupted import data
ErrDecryptionFailed error // Email decryption failed
ErrSignatureInvalid error // Signature verification failed
ErrRateLimited error // API rate limit exceeded
)
inbox, err := client.CreateInbox(ctx)
if errors.Is(err, vaultsandbox.ErrUnauthorized) {
log.Fatal("Invalid API key")
}
if errors.Is(err, vaultsandbox.ErrRateLimited) {
log.Println("Rate limited, retrying...")
}

Represents an HTTP error from the VaultSandbox API.

type APIError struct {
StatusCode int // HTTP status code
Message string // Error message from server
RequestID string // Request ID for support
}

Represents a network-level failure.

type NetworkError struct {
Err error // Underlying network error
}

Indicates signature verification failed, including potential server key mismatch (MITM detection).

type SignatureVerificationError struct {
Message string
}

Used to identify which resource type an error relates to:

type ResourceType string
const (
ResourceUnknown ResourceType = "" // Resource type not specified
ResourceInbox ResourceType = "inbox" // Error relates to an inbox
ResourceEmail ResourceType = "email" // Error relates to an email
)
email, err := inbox.GetEmail(ctx, emailID)
if err != nil {
var apiErr *vaultsandbox.APIError
if errors.As(err, &apiErr) {
log.Printf("API error %d: %s (request: %s)",
apiErr.StatusCode, apiErr.Message, apiErr.RequestID)
}
var netErr *vaultsandbox.NetworkError
if errors.As(err, &netErr) {
log.Printf("Network error: %v", netErr.Err)
}
}

Here’s a complete example showing typical client usage:

package main
import (
"context"
"fmt"
"log"
"os"
"regexp"
"time"
vaultsandbox "github.com/vaultsandbox/client-go"
)
func main() {
ctx := context.Background()
// Create client
client, err := vaultsandbox.New(
os.Getenv("VAULTSANDBOX_API_KEY"),
vaultsandbox.WithBaseURL(os.Getenv("VAULTSANDBOX_URL")),
vaultsandbox.WithDeliveryStrategy(vaultsandbox.StrategyAuto),
vaultsandbox.WithRetries(5),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Verify API key
if err := client.CheckKey(ctx); err != nil {
log.Fatal("Invalid API key:", err)
}
// Get server info
info := client.ServerInfo()
fmt.Printf("Connected to VaultSandbox (default TTL: %v)\n", info.DefaultTTL)
// Create inbox
inbox, err := client.CreateInbox(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created inbox: %s\n", inbox.EmailAddress())
// Export for later use
if err := client.ExportInboxToFile(inbox, "./inbox-backup.json"); err != nil {
log.Fatal(err)
}
// Wait for email
email, err := inbox.WaitForEmail(ctx,
vaultsandbox.WithWaitTimeout(30*time.Second),
vaultsandbox.WithSubjectRegex(regexp.MustCompile(`Test`)),
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Received: %s\n", email.Subject)
// Clean up
if err := inbox.Delete(ctx); err != nil {
log.Fatal(err)
}
// Delete any other orphaned inboxes
deleted, err := client.DeleteAllInboxes(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Cleaned up %d total inboxes\n", deleted)
}