Skip to content

VaultSandboxClient API

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

VaultSandboxClient(
api_key: str,
*,
base_url: str = "https://smtp.vaultsandbox.com",
timeout: int = 30000,
max_retries: int = 3,
retry_delay: int = 1000,
retry_on_status_codes: tuple[int, ...] | None = None,
strategy: DeliveryStrategyType = DeliveryStrategyType.AUTO,
polling_interval: int = 2000,
polling_max_backoff: int = 30000,
sse_reconnect_interval: int = 5000,
sse_max_reconnect_attempts: int = 10,
)

Creates a new VaultSandbox client instance.

ParameterTypeRequiredDefaultDescription
api_keystrYes-Your API authentication key
base_urlstrNohttps://smtp.vaultsandbox.comGateway URL
timeoutintNo30000HTTP request timeout in milliseconds
max_retriesintNo3Maximum retry attempts for HTTP requests
retry_delayintNo1000Base delay in milliseconds between retries
retry_on_status_codestuple[int, ...]No(408, 429, 500, 502, 503, 504)HTTP status codes that trigger a retry
strategyDeliveryStrategyTypeNoAUTOEmail delivery strategy
polling_intervalintNo2000Polling interval in milliseconds
polling_max_backoffintNo30000Maximum backoff delay in milliseconds
sse_reconnect_intervalintNo5000Initial delay before SSE reconnection (ms)
sse_max_reconnect_attemptsintNo10Maximum SSE reconnection attempts
import os
from vaultsandbox import VaultSandboxClient, DeliveryStrategyType
async with VaultSandboxClient(
api_key=os.environ["VAULTSANDBOX_API_KEY"],
base_url="https://smtp.vaultsandbox.com",
strategy=DeliveryStrategyType.AUTO,
max_retries=5,
retry_delay=2000,
) as client:
inbox = await client.create_inbox()

The recommended way to use the client is with Python’s async with context manager, which ensures proper cleanup of resources:

async with VaultSandboxClient(api_key="your-api-key") as client:
inbox = await client.create_inbox()
email = await inbox.wait_for_email()
print(f"Received: {email.subject}")

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

async def create_inbox(
self,
options: CreateInboxOptions | None = None,
) -> Inbox
  • options (optional): Configuration for the inbox
@dataclass
class CreateInboxOptions:
ttl: int | None = None
email_address: str | None = None
PropertyTypeDescription
ttlint | NoneTime-to-live for the inbox in seconds (min: 60, max: 604800, default: server’s defaultTtl)
email_addressstr | NoneRequest a specific email address (max 254 chars, e.g., [email protected])

Inbox - The created inbox instance

from vaultsandbox import CreateInboxOptions
# Create inbox with default settings
inbox = await client.create_inbox()
print(inbox.email_address)
# Create inbox with custom TTL (1 hour)
inbox = await client.create_inbox(CreateInboxOptions(ttl=3600))
# Request specific email address
inbox = await client.create_inbox(
CreateInboxOptions(email_address="[email protected]")
)
  • ApiError - API-level error (invalid request, permission denied)
  • NetworkError - Network connection failure
  • InboxAlreadyExistsError - Requested email address is already in use

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

async def delete_all_inboxes(self) -> int

int - Number of inboxes deleted

deleted = await client.delete_all_inboxes()
print(f"Deleted {deleted} inboxes")

Use this in test cleanup to avoid orphaned inboxes:

@pytest.fixture
async def client():
async with VaultSandboxClient(api_key=api_key) as client:
yield client
# Clean up all inboxes after tests
deleted = await client.delete_all_inboxes()
if deleted > 0:
print(f"Cleaned up {deleted} orphaned inboxes")

Retrieves information about the VaultSandbox Gateway server.

async def get_server_info(self) -> ServerInfo

ServerInfo - Server information object

@dataclass
class ServerInfo:
server_sig_pk: str
algs: dict[str, str]
context: str
max_ttl: int
default_ttl: int
sse_console: bool
allowed_domains: list[str]
PropertyTypeDescription
server_sig_pkstrBase64URL-encoded server signing public key for ML-DSA-65
algsdict[str, str]Cryptographic algorithms supported by the server
contextstrContext string for the encryption scheme
max_ttlintMaximum time-to-live for inboxes in seconds
default_ttlintDefault time-to-live for inboxes in seconds
sse_consoleboolWhether the server SSE console is enabled
allowed_domainslist[str]List of domains allowed for inbox creation
info = await client.get_server_info()
print(f"Encryption: {info.algs['kem']}")
print(f"Max TTL: {info.max_ttl}s, Default TTL: {info.default_ttl}s")
print(f"Allowed domains: {', '.join(info.allowed_domains)}")

Validates the API key with the server.

async def check_key(self) -> bool

bool - True if the API key is valid

is_valid = await client.check_key()
if not is_valid:
raise ValueError("Invalid API key")

Useful for verifying configuration before running tests:

import pytest
import os
from vaultsandbox import VaultSandboxClient
@pytest.fixture(scope="session")
async def validated_client():
async with VaultSandboxClient(
api_key=os.environ["VAULTSANDBOX_API_KEY"]
) as client:
is_valid = await client.check_key()
if not is_valid:
pytest.fail("VaultSandbox API key is invalid")
yield client

Monitors multiple inboxes simultaneously and provides callbacks when new emails arrive.

def monitor_inboxes(self, inboxes: list[Inbox]) -> InboxMonitor
  • inboxes: List of inbox instances to monitor

InboxMonitor - Monitor instance for inbox monitoring

inbox1 = await client.create_inbox()
inbox2 = await client.create_inbox()
monitor = client.monitor_inboxes([inbox1, inbox2])
@monitor.on_email
async def handle_email(inbox: Inbox, email: Email):
print(f"New email in {inbox.email_address}: {email.subject}")
await monitor.start()
# Later, stop monitoring
await monitor.unsubscribe()

See InboxMonitor API for more details.


Exports an inbox’s data and encryption keys for backup or sharing. The exported data includes sensitive key material and should be treated as confidential.

def export_inbox(self, inbox_or_email: Inbox | str) -> ExportedInbox
  • inbox_or_email: Inbox instance or email address string to export

ExportedInbox - Serializable inbox data including keys

@dataclass
class ExportedInbox:
version: int # Export format version (always 1)
email_address: str
expires_at: str
inbox_hash: str
server_sig_pk: str # Base64url-encoded
secret_key: str # Base64url-encoded (SENSITIVE!)
exported_at: str

Note: The public key is derived from the secret key during import.

import json
inbox = await client.create_inbox()
exported_data = client.export_inbox(inbox)
# Save for later use (treat as sensitive!)
print(json.dumps(vars(exported_data), indent=2))

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.

async def import_inbox(self, data: ExportedInbox) -> Inbox
  • data: Previously exported inbox data

Inbox - The imported inbox instance

import json
from vaultsandbox import ExportedInbox
# Load exported data
with open("inbox-backup.json") as f:
data = json.load(f)
exported = ExportedInbox(
version=data["version"],
email_address=data["emailAddress"],
expires_at=data["expiresAt"],
inbox_hash=data["inboxHash"],
server_sig_pk=data["serverSigPk"],
secret_key=data["secretKey"],
exported_at=data.get("exportedAt", ""),
)
inbox = await client.import_inbox(exported)
print(f"Imported inbox: {inbox.email_address}")
# Use inbox normally
emails = await inbox.list_emails()
  • UnsupportedVersionError - Export version is not supported
  • InboxAlreadyExistsError - Inbox is already imported in this client
  • InvalidImportDataError - Import data is invalid or corrupted
  • ApiError - Server rejected the import (inbox may not exist)

Exports an inbox to a JSON file on disk.

async def export_inbox_to_file(
self,
inbox_or_email: Inbox | str,
file_path: str | Path,
) -> None
  • inbox_or_email: Inbox instance or email address string to export
  • file_path: Path where the JSON file will be written
inbox = await client.create_inbox()
# Export to file
await client.export_inbox_to_file(inbox, "./backup/inbox.json")
print("Inbox exported to ./backup/inbox.json")

Imports an inbox from a JSON file.

async def import_inbox_from_file(self, file_path: str | Path) -> Inbox
  • file_path: Path to the exported inbox JSON file

Inbox - The imported inbox instance

# Import from file
inbox = await client.import_inbox_from_file("./backup/inbox.json")
print(f"Imported inbox: {inbox.email_address}")
# Monitor for new emails
subscription = await inbox.on_new_email(
lambda email: print(f"New email: {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.

async def close(self) -> None
client = VaultSandboxClient(api_key=api_key)
try:
inbox = await client.create_inbox()
# Use inbox...
finally:
await client.close()

Always close the client when done, especially in long-running processes. The recommended approach is to use the context manager:

async with VaultSandboxClient(api_key=api_key) as client:
inbox = await client.create_inbox()
# Use inbox...
# Client automatically closed

Or with pytest fixtures:

import pytest
from vaultsandbox import VaultSandboxClient
@pytest.fixture
async def client():
async with VaultSandboxClient(api_key=api_key) as client:
yield client

The InboxMonitor class allows you to monitor multiple inboxes simultaneously.

inbox1 = await client.create_inbox()
inbox2 = await client.create_inbox()
monitor = client.monitor_inboxes([inbox1, inbox2])

Register a callback for new emails. Can be used as a decorator.

def on_email(self, callback: Callable[[Inbox, Email], Any]) -> InboxMonitor
  • callback: Function to call when new emails arrive. Receives (inbox, email) as arguments.

InboxMonitor - Self for method chaining

# Using as a decorator
@monitor.on_email
async def handle_email(inbox: Inbox, email: Email):
print(f"Email received in {inbox.email_address}")
print(f"Subject: {email.subject}")
# Or using method chaining
monitor.on_email(handle_email).on_email(another_handler)

Start monitoring inboxes.

async def start(self) -> InboxMonitor

InboxMonitor - Self for method chaining

await monitor.start()

Stops monitoring all inboxes and cleans up resources.

async def unsubscribe(self) -> None
monitor = client.monitor_inboxes([inbox1, inbox2])
# Use monitor...
# Stop monitoring
await monitor.unsubscribe()
import asyncio
from vaultsandbox import VaultSandboxClient, Inbox, Email
async def monitor_multiple_inboxes():
async with VaultSandboxClient(api_key=api_key) as client:
# Create multiple inboxes
inbox1 = await client.create_inbox()
inbox2 = await client.create_inbox()
print(f"Inbox 1: {inbox1.email_address}")
print(f"Inbox 2: {inbox2.email_address}")
# Monitor both inboxes
monitor = client.monitor_inboxes([inbox1, inbox2])
@monitor.on_email
async def handle_email(inbox: Inbox, email: Email):
print(f"\nNew email in {inbox.email_address}:")
print(f" Subject: {email.subject}")
print(f" From: {email.from_address}")
await monitor.start()
# Wait for emails to arrive...
await asyncio.sleep(60)
# Clean up
await monitor.unsubscribe()
await inbox1.delete()
await inbox2.delete()
asyncio.run(monitor_multiple_inboxes())

Here’s a complete example showing typical client usage:

import asyncio
import os
from vaultsandbox import VaultSandboxClient, WaitForEmailOptions
import re
async def main():
# Create client
async with VaultSandboxClient(
api_key=os.environ["VAULTSANDBOX_API_KEY"],
max_retries=5,
) as client:
# Verify API key
is_valid = await client.check_key()
if not is_valid:
raise ValueError("Invalid API key")
# Get server info
info = await client.get_server_info()
print(f"Connected to VaultSandbox (default TTL: {info.default_ttl}s)")
# Create inbox
inbox = await client.create_inbox()
print(f"Created inbox: {inbox.email_address}")
# Export for later use
await client.export_inbox_to_file(inbox, "./inbox-backup.json")
# Wait for email
email = await inbox.wait_for_email(
WaitForEmailOptions(
timeout=30000,
subject=re.compile(r"Test"),
)
)
print(f"Received: {email.subject}")
# Clean up
await inbox.delete()
# Delete any other orphaned inboxes
deleted = await client.delete_all_inboxes()
print(f"Cleaned up {deleted} total inboxes")
asyncio.run(main())