One workspace per your customer. Sandbox Platform handles the auth mesh, sandbox isolation, billing meters, and audit. You build the customer-facing product UI on top. Customers see your brand, your workflows, your features — under the hood it's our infrastructure they don't have to think about. Time to MVP: 2-4 weeks. Build-it-yourself equivalent: 4-6 months and a platform team.
Who this is for
You're an early-stage founder or engineering lead at a vertical-AI SaaS. Your product looks like:
- Harvey for [a vertical] — AI workers for legal, finance, healthcare, real estate, accounting, etc.
- An ops automation product — your customer's AP, support, marketing-ops gets AI workers
- A workflow tool — your customer designs workflows; AI workers execute
- A vertical agent marketplace — customers browse/hire pre-built agents from your catalog
The common shape: each of your customers needs isolated compute, isolated data, isolated billing, and the ability to see only their own audit trail. That's the multi-tenancy problem.
Why AI agent multi-tenancy is harder than traditional SaaS
Multi-tenancy for CRUD apps is solved. You add a tenant_id column, filter every query, and you're done. AI agents break this model in five ways that don't appear until you're in production with real customers.
| Scenario | Shared-infrastructure approach | Per-tenant sandbox approach |
|---|---|---|
| Data isolation | Tenant A's Claude Code process runs on the same server as Tenant B's. A misdirected file write, a leaked environment variable, or a path traversal in a tool means Tenant A's agent can read Tenant B's files. Shared filesystem = shared risk. | Each tenant's agent runs in its own container with a fresh ephemeral filesystem. No shared volumes, no shared process space. A file write in Tenant A's sandbox cannot touch Tenant B's sandbox — they're on different kernel namespaces. |
| Rate limiting | One tenant runs a batch job that hammers the LLM API, saturates the network egress, and pushes CPU to 95%. Every other tenant on the same host degrades. The noisy-neighbor problem is catastrophic for an AI-heavy workload where one agent session can consume 40x a quiet tenant's resources. | Per-sandbox network controls cap egress bandwidth. Per-workspace limits cap concurrent sandboxes. Tenant A's heavy batch job runs in its own compute envelope and cannot crowd out Tenant B's interactive session on the same platform. |
| Compliance audit trails | All agent activity logs into one log stream with a tenant_id field. When an enterprise customer asks for their audit log for a SOC 2 audit, you have to query-filter from a mixed stream, which mixes with your own operational logs. Auditability is hard; log isolation is impossible. |
Every action — file read, file write, API call, command execution — is logged with the workspace context and can be streamed to the tenant's own SIEM (Datadog, Splunk, S3). Enterprise customers get a clean per-tenant audit stream that satisfies their own compliance team. |
| Agent customization | One global system prompt or CLAUDE.md for all tenants. Tenant A is an accounting firm; Tenant B is a law firm. You either make the prompt generic (bad for both) or you write complex branching logic in the prompt to handle all customer types. Tenant A's business rules leak into Tenant B's agent context via shared prompt fragments. | Each tenant's sandbox is launched with a tenant-specific CLAUDE.md. Tenant A gets their chart of accounts, their escalation contacts, their billing rules. Tenant B gets their jurisdiction-specific legal references, their client matter conventions. Zero cross-contamination. |
| Cost allocation | Your platform bill is one number. Attributing it to individual tenants requires instrumentation you probably didn't build up-front: timing agent sessions, estimating LLM tokens per tenant, guessing compute cost from shared host utilization. Unit economics are impossible to calculate; high-consumption tenants subsidized by low-consumption tenants. | Every sandbox launch is tagged with tenant_id, workspace_id, and your own metadata. The billing API returns per-workspace compute hours, LLM token counts, and dollar amounts. Cost per tenant is a single API call. You know exactly which tenants are profitable and which are underwater. |
These five problems are why companies like Cursor built dedicated per-user cloud-agent infrastructure, and why GitHub Copilot Workspace uses per-repo isolation rather than a shared execution pool. Isolation is a first-class feature, not an afterthought.
The architecture
Three layers:
- Your product layer. Customer-facing UI, your business logic, your branding, your unique workflows. You own this.
- Sandbox Platform layer. Workspace management, sandbox provisioning, billing meters, audit, model integrations. You consume this via API.
- Infrastructure layer. AWS, EC2, cloud containers, etc. We handle this; you don't see it.
The org / tenant model
One workspace per customer. Inside each workspace:
- Members. Your customer's team — invited by them, role-managed by them.
- AI employees. Sandboxes belonging to that customer's workspace, isolated from other customers.
- Audit log. Visible only to that customer's authorized members.
- Billing meter. Per-workspace cost tracking that you can roll up into your own customer billing.
- Cost limits. Per-workspace budgets that fence cost runaway.
Customer onboarding flow
# When a customer signs up to YOUR product: def create_customer(customer_name, customer_email, plan): # 1. Create the workspace in Sandbox Platform workspace = sandbox_api.post("/api/workspaces", json={ "name": customer_name, "plan": plan, # maps to a tier on our side "cost_limit_monthly": PLAN_LIMITS[plan], "metadata": {"your_customer_id": customer_name}, }) # 2. Invite the customer's primary admin sandbox_api.post(f"/api/workspaces/{workspace['id']}/members", json={ "email": customer_email, "role": "admin", }) # 3. Pre-provision the AI employees this customer's plan includes for role in PLAN_AGENTS[plan]: sandbox_api.post(f"/api/workspaces/{workspace['id']}/agents", json={ "name": role["name"], "template": role["template"], "config": role["config"], }) return workspace["id"]
That's the full onboarding. ~30 seconds end-to-end. The customer gets an email invitation; they click it; they're in your product; their AI workers are already provisioned.
Per-customer billing
Sandbox Platform meters every customer's usage separately. Roll-up patterns:
| Pricing model | How you bill |
|---|---|
| Flat tier | You charge $X/month; you absorb the variability of compute cost (capped by per-workspace limits) |
| Pass-through usage | You charge usage + margin; Sandbox Platform's per-workspace billing API gives you the exact number to invoice |
| Hybrid (tier + overage) | Flat for Y hours/month included; per-hour overage above. Most common pattern |
| Per-agent | $Z/agent/month; one agent = one isolated sandbox; predictable |
The billing API exposes hourly and daily usage; pull on your billing cycle and bill via Stripe / Chargebee / your billing provider.
Isolation guarantees
Multi-tenancy is meaningless without strong isolation. The platform's guarantees:
- Sandbox isolation. Each customer's sandboxes run in their own EC2 instances or cloud containers; they cannot see or touch each other.
- Network isolation. Per-workspace VPC or per-allocation security groups; customer A's agent cannot make outbound calls to customer B's allocation.
- Data isolation. Per-workspace storage; per-workspace audit log; cross-workspace access requires explicit cross-tenant permissions (admin-only).
- Identity isolation. Per-workspace user directory; auth tokens scoped to a single workspace.
- Compliance: SOC 2 Type II, GDPR-compliant by default. HIPAA available with BAA.
The three-layer isolation model
Sandbox isolation is not a single mechanism — it is three independent layers that together make tenant data bleed structurally impossible. Understanding the layers helps you explain the security posture to enterprise customers.
Layer 1: Process isolation
Each tenant's agent runs in its own container — a separate Linux kernel namespace, a separate process tree, and separate memory address space. An agent crash in Tenant A cannot propagate to Tenant B's process, and Tenant A's Claude Code process cannot ptrace Tenant B's process or inspect its memory. The container boundary is enforced by the kernel, not by application code.
This is why LangChain ReAct loops running multiple tenants in threads inside a single Python process are inadequate for production SaaS. A single unhandled exception, a shared mutable global, or a threading bug can cross tenant boundaries. The sandbox model pushes that boundary down to the kernel level.
Layer 2: Filesystem isolation
Each sandbox gets a fresh ephemeral filesystem provisioned at launch. There are no shared volumes between tenants unless you explicitly configure one (which you would only do for shared read-only reference data under your control, never for customer data). A tenant's agent writing to /workspace/customer_data.json is writing into a filesystem visible only inside that sandbox.
When the sandbox terminates, the filesystem is wiped. A subsequent sandbox — even for the same tenant — starts from a clean base image unless you explicitly persist files to durable storage. This default-ephemeral posture is the right security default for agentic workloads.
Layer 3: Network isolation and egress control
Per-sandbox egress controls define which external APIs an agent is allowed to call. A prompt-injection attack that tries to exfiltrate customer data by having the agent call a malicious webhook is stopped at the network layer if that destination is not on the allowlist.
Configure the egress allowlist at sandbox launch time:
# Launch a tenant sandbox with strict egress controls sandbox = sandbox_api.post("/api/sandboxes", json={ "name": f"agent-{tenant_id}", "workspace_id": workspace_id, "instance_type": "ab0t.medium", "egress_policy": { "default": "deny", # block everything by default "allow": [ "api.anthropic.com", # LLM provider "api.openai.com", "your-api.yourproduct.com", # your product's API "api.quickbooks.com", # tenant-permitted integrations "api.xero.com", ], }, "metadata": {"tenant_id": tenant_id}, })
With this configuration, the agent can call the LLM, call your product API, and call the two accounting integrations the tenant authorized. It cannot call anything else — not S3 pre-signed URLs for data exfiltration, not attacker-controlled webhooks, not internal AWS metadata endpoints. The egress allowlist is your defense-in-depth layer against prompt injection.
The admin dashboard customers self-serve
Customers can:
- Add/remove team members
- View their AI workers, configure their CLAUDE.md, set per-agent cost caps
- See their audit log filtered to their workspace
- View their usage / cost dashboard
- Set workspace-level budgets
- Export audit log for their own compliance
- Manage their own credentials / integrations (Slack, EHR, etc.)
You can either (a) embed our dashboard in an iframe, (b) build your own UI calling our API, or (c) hybrid (your branding + our component library). Most SaaS founders we work with do (c) — branded UI, our heavy lifting underneath.
Per-tenant CLAUDE.md customization
The most powerful multi-tenant feature is not compute isolation — it is context isolation. Each tenant's agent can run with a completely different set of instructions, domain knowledge, and behavioral rules injected at launch time via CLAUDE.md.
This is how Cursor's multi-tenant cloud-agent infrastructure works (each user gets a context window tailored to their repo and settings) and how GitHub Copilot Workspace achieves per-repo isolation (each workspace gets a CLAUDE.md-equivalent with the repo's conventions and the user's preferences). The pattern is standard for any serious multi-tenant AI product.
The template injection pattern
Maintain two pieces in your product database:
- Base CLAUDE.md — your product's core instructions, the agent persona, the standard operating procedures that apply to all tenants.
- Tenant-specific context — the data schema, tool integrations, business rules, and escalation contacts that are unique to each customer.
At sandbox launch time, merge them and inject the result into the sandbox:
def build_claude_md(tenant: dict) -> str: """Compose a tenant-specific CLAUDE.md from base + overrides.""" base = load_base_claude_md() # your product's core instructions tenant_context = f""" ## Tenant Context **Organization:** {tenant['name']} **Industry:** {tenant['industry']} **Data Schema:** {tenant['schema_description']} **Approved Integrations:** {', '.join(tenant['integrations'])} ### Business Rules {tenant['business_rules']} ### Escalation Contacts - Primary: {tenant['escalation_email']} - Slack: {tenant['escalation_slack']} ### Data Handling - Never export data outside: {', '.join(tenant['approved_egress_domains'])} - PII fields: {', '.join(tenant['pii_fields'])} - Retention policy: {tenant['retention_days']} days """ return base + tenant_context def launch_tenant_sandbox(tenant_id: str, task: str) -> dict: tenant = db.get_tenant(tenant_id) claude_md = build_claude_md(tenant) return sandbox_api.post("/api/sandboxes", json={ "name": f"task-{tenant_id}", "workspace_id": tenant["workspace_id"], "claude_md": claude_md, # injected into /home/user/CLAUDE.md "initial_task": task, "metadata": {"tenant_id": tenant_id}, })
The claude_md parameter is written to /home/user/CLAUDE.md inside the sandbox before the agent starts. Claude Code reads it automatically on startup. The agent begins its session already aware of the tenant's data schema, their approved integrations, and their specific business rules — with no need for a warmup conversation to establish context.
What to put in the per-tenant context block
| Context type | What it contains | Why it matters |
|---|---|---|
| Data schema | Table names, field names, data types, relationships | Agent generates correct queries without hallucinating column names |
| Tool integrations | Which APIs are available, authentication patterns, rate limits | Agent knows what it can call without trial-and-error |
| Business rules | Approval thresholds, jurisdiction-specific rules, workflow gates | Agent applies the right rules for this tenant without cross-tenant contamination |
| Escalation contacts | Who to notify when uncertain, approval-required thresholds | Agent escalates to the right human, not a generic support address |
| PII and data handling | Which fields are sensitive, retention policies, export restrictions | Agent handles sensitive data correctly without a separate policy enforcement layer |
| Tenant persona | Company name, preferred communication style, terminology | Agent uses the tenant's vocabulary and tone rather than generic language |
Cost allocation and unit economics
Building a profitable AI product requires knowing your cost per tenant. Shared-infrastructure deployments make this impossible. Per-tenant sandboxes make it trivial.
Tagging for attribution
Every sandbox launch should include your tenant metadata:
sandbox_api.post("/api/sandboxes", json={ "name": f"agent-{tenant_id}", "workspace_id": workspace_id, "metadata": { "tenant_id": tenant_id, "tenant_plan": "pro", # for per-tier cost analysis "task_type": "ap_processing", # for per-workflow cost analysis "triggered_by": "webhook", # event source "your_invoice_id": invoice_id, # tie back to your billing }, })
Pull per-tenant cost reports on your billing cycle:
def get_tenant_cost(workspace_id: str, start: str, end: str) -> dict: """Returns compute_hours, llm_tokens, and total_usd for the period.""" return sandbox_api.get( f"/api/workspaces/{workspace_id}/billing", params={"start": start, "end": end, "granularity": "daily"} )
The unit economics of AI agent features
Use this model to price your AI features:
| Cost component | Typical range | Notes |
|---|---|---|
| Compute cost per active session | $0.04–$0.16/hr (ab0t.micro–ab0t.medium) | Active only; idle sandbox = $0 |
| LLM API cost per task | $0.003–$0.05 per task | Highly variable by task complexity and model |
| Amortized warm-pool cost | $0.001–$0.005 per session | Only if you use warm pools for instant startup |
| Total cost per completed task | $0.01–$0.25 | Median ~$0.05 for a typical agent task |
Pricing structures that work for AI agent features:
- Per-seat: $X/user/month with a task cap. Predictable for the customer; you absorb usage variability. Set a generous cap and enforce it with per-workspace limits.
- Per-usage: $Y per task or per agent-hour. Aligns incentives but requires more explanation in sales. Works well for high-value, low-frequency tasks.
- Hybrid: Flat fee includes N tasks/month; overage billed at Z per task. Most common structure. Customers get predictability; you capture upside from heavy users.
- Per-outcome: Bill per document processed, per invoice paid, per case resolved. Requires you to track outcomes, but aligns with customer ROI. High-willingness-to-pay if the task value is clear.
The key insight: at $0.05/task median cost, you can price at $1–5/task and still have a 20–100x gross margin on the compute. The bottleneck on unit economics is usually LLM API cost (at scale, negotiate enterprise contracts with Anthropic and OpenAI) and your own human support cost per customer, not sandbox compute.
Compliance and audit trails per tenant
Enterprise customers will ask: "Can we get our audit logs in our SIEM?" and "What controls do you have for SOC 2?" These questions come up before the sales cycle closes, not after. Having real answers is a competitive advantage.
What the platform provides
| Control | What it covers | Your responsibility |
|---|---|---|
| Encryption at rest | All sandbox filesystems, stored files, audit logs — AES-256 | Manage your own database encryption |
| Encryption in transit | All API calls, LLM provider calls — TLS 1.3 | Ensure your product API uses TLS |
| Access logging | Every API call, every sandbox action, every file operation — logged with workspace context | Define who in your team has access to the admin API |
| Network isolation | Per-sandbox egress controls, per-workspace VPC isolation | Define the egress allowlist correctly at launch time |
| Availability | 99.9% SLA on the control plane; multi-AZ sandbox provisioning | Design your product for sandbox restart failures |
| SOC 2 Type II | Covers the platform infrastructure, the control plane, and the audit log system | Your application layer, your data models, your access controls |
Streaming per-tenant audit logs
Enterprise customers often have a compliance requirement to receive their own audit logs in their own SIEM. Configure the workspace's audit stream destination at provisioning time:
workspace = sandbox_api.post("/api/workspaces", json={ "name": customer_name, "plan": "enterprise", "cost_limit_monthly": 5000, "audit_stream": { "destination": "s3", "s3_bucket": customer["audit_bucket"], "s3_prefix": f"audit/{customer_id}/", "format": "jsonl", # one event per line, easy to ingest "include_events": [ "sandbox.created", "sandbox.terminated", "file.read", "file.write", "command.executed", "api.called", ], }, "metadata": {"your_customer_id": customer_id}, })
Each audit event includes: event_type, timestamp, workspace_id, sandbox_id, actor (which agent or user triggered the event), resource (the file path, API endpoint, or command), and outcome (success or failure). The stream goes directly to the customer's S3 bucket in near-real-time. Their compliance team can ingest it into their SIEM without routing through your product.
HIPAA workspaces
For healthcare tenants, configure HIPAA posture at workspace creation:
workspace = sandbox_api.post("/api/workspaces", json={ "name": customer_name, "compliance_tier": "hipaa", # requires BAA in your account "allowed_models": [ "claude-3-7-sonnet-20250219", # Anthropic Business Associate Agreement ], "data_residency": "us-east-1", # PHI must not leave US regions "phi_scanning": True, # automatic PHI detection in outputs })
Operational patterns for agent fleets at scale
Running 5 tenants is a proof-of-concept. Running 500 tenants is an operations problem. Here are the patterns that don't appear in the MVP phase but matter when you have a real customer base.
Health monitoring per tenant
Monitor at two levels: platform-level (is the sandbox running?) and agent-level (is the agent making progress?).
import asyncio from datetime import datetime, timedelta async def check_tenant_health(workspace_id: str) -> dict: sandboxes = sandbox_api.get( f"/api/workspaces/{workspace_id}/sandboxes", params={"status": "running"} ) alerts = [] for sb in sandboxes: # Stuck-agent detection: last activity > 30 min ago last_activity = datetime.fromisoformat(sb["last_activity_at"]) if datetime.utcnow() - last_activity > timedelta(minutes=30): alerts.append({ "sandbox_id": sb["id"], "alert": "stuck", "last_activity": sb["last_activity_at"], }) # High CPU: agent burning compute without progress if sb["cpu_percent"] > 90: alerts.append({ "sandbox_id": sb["id"], "alert": "high_cpu", "cpu_percent": sb["cpu_percent"], }) return {"workspace_id": workspace_id, "alerts": alerts}
Rolling CLAUDE.md updates without downtime
When you publish a new version of your base CLAUDE.md (fixing a bug in the agent's behavior, adding a new capability, updating a compliance requirement), you need to roll it out without disrupting active sessions.
def publish_claude_md_update(new_version: str): """ Update the CLAUDE.md template version for all tenants. Takes effect on the next sandbox launch per tenant. Does not restart running sandboxes. """ db.set_base_claude_md_version(new_version) # Log the update for your own audit trail db.log_template_update({ "version": new_version, "published_at": datetime.utcnow().isoformat(), "published_by": "ops-team", }) # Optionally force-refresh idle tenants' next-launch template # Running sandboxes keep their current CLAUDE.md until they terminate print(f"CLAUDE.md v{new_version} published. Takes effect on next launch per tenant.")
Noisy-tenant isolation and throttling
When a tenant's agents are consuming disproportionate resources — a runaway batch job, a misconfigured agent in an infinite retry loop — throttle that tenant without affecting others:
def throttle_tenant(workspace_id: str, reason: str): """Reduce concurrent sandbox limit for a workspace in-flight.""" sandbox_api.patch(f"/api/workspaces/{workspace_id}", json={ "max_concurrent_sandboxes": 1, # down from their plan limit "throttle_reason": reason, }) notify_ops_team(f"Throttled workspace {workspace_id}: {reason}")
Per-tier concurrent sandbox limits
Configure different resource envelopes at workspace creation to enforce your pricing tiers:
PLAN_CONFIG = { "free": { "max_concurrent_sandboxes": 1, "max_sandboxes_per_day": 10, "max_compute_hours_per_month": 5, "cost_limit_monthly": 5, # $5 hard cap "allowed_instance_types": ["ab0t.micro"], }, "pro": { "max_concurrent_sandboxes": 5, "max_sandboxes_per_day": 100, "max_compute_hours_per_month": 100, "cost_limit_monthly": 200, "allowed_instance_types": ["ab0t.micro", "ab0t.medium"], }, "enterprise": { "max_concurrent_sandboxes": 0, # 0 = no cap "max_sandboxes_per_day": 0, "max_compute_hours_per_month": 0, "cost_limit_monthly": 0, "allowed_instance_types": ["ab0t.micro", "ab0t.medium", "ab0t.large", "ab0t.gpu"], }, } def create_workspace(customer_name: str, plan: str) -> dict: config = PLAN_CONFIG[plan] return sandbox_api.post("/api/workspaces", json={ "name": customer_name, "plan": plan, **config, })
Common mistakes from the field
These are the mistakes we see most often from SaaS founders building their first multi-tenant AI product. All of them are avoidable with upfront architecture decisions.
1. Shared CLAUDE.md across all tenants
The most common mistake. A founder builds one CLAUDE.md that describes the product generically and ships it to all customers. Tenant A is an accounting firm with QBO; Tenant B is a law firm with Clio. The agent tries to use accounting logic when helping the law firm, and legal reasoning when helping the accountant. Customers churn because the agent "doesn't understand our business."
Fix: tenant-specific CLAUDE.md injection from day one. Even a minimal per-tenant context block (industry, key tools, escalation contact) dramatically improves agent relevance.
2. Not tagging sandbox launches with tenant_id
You launch sandboxes without metadata. Six months later, you're billing customers and can't answer "how much compute did Tenant A use this month?" You're guessing, or you're manually correlating timestamps from logs. The billing API is only useful if you tag at launch time.
Fix: make tenant_id a required field in your sandbox launch wrapper. Fail loudly if it is missing. Add it to every sandbox launch call from the beginning.
3. Unrestricted egress
The sandbox's agent can call any URL on the internet. A customer's employee crafts a clever prompt that causes the agent to send their company's data to an attacker-controlled endpoint. Or a legitimate agent goes rogue after a model update and starts making unexpected API calls.
Fix: egress allowlist as the default, not the exception. Default-deny outbound. Add to the allowlist explicitly. The five-minute overhead at launch time is worth it against the cost of a data breach.
4. Building the multi-tenant layer inside the agent instead of at the sandbox layer
Some founders try to implement multi-tenancy inside the agent's prompt: "You are helping Tenant A. Do not mention Tenant B's data." This is the wrong abstraction. The agent is not a reliable enforcement boundary. A sufficiently clever prompt can override in-context instructions. The sandbox IS the isolation boundary — use it.
Fix: one workspace per tenant, one sandbox per task. Let the platform enforce isolation. Use the CLAUDE.md for domain customization, not for access control.
5. Not planning for tenant offboarding
A customer churns. You mark them inactive in your database, but their workspace is still running. Their sandboxes are still billable. Their API keys are still valid. Three months later you find the cost in your bill.
Fix: build a workspace teardown flow before you launch. When a customer cancels: terminate all running sandboxes, revoke all API keys scoped to that workspace, call DELETE /api/workspaces/{id} with confirm_delete: true, confirm the deletion event in your audit log.
6. Using a single API key for all tenant model calls
All your tenants' agents share one Anthropic API key. One high-volume tenant hits the rate limit and every other tenant's agents start failing. You can't see per-tenant model costs. You can't apply different model versions to different tiers.
Fix: per-workspace model API keys. Pass a workspace-specific key (or your own per-tenant key) in the sandbox launch config. Or use the platform's built-in per-workspace model routing, which handles rate limit isolation automatically.
Build-vs-buy: the timeline
| Option | Timeline | Headcount | Total cost (year 1) |
|---|---|---|---|
| Build it yourself in the cloud / k8s | 4-6 months to MVP, ~12 months to production-ready | 2 platform engineers | $300-450K (salary + AWS) |
| Build on Sandbox Platform | 2-4 weeks to MVP | 1 product engineer (you) | ~$15K (platform fees, scales with usage) |
The build-it-yourself path is sometimes the right call — you have a unique technical thesis, you need to control the runtime, you're at scale where the platform fee dwarfs hiring engineers. For most early-stage SaaS, the math is dramatically in favor of building on the platform.
What the build-it-yourself path actually includes, so you can compare honestly:
- Kubernetes or cloud containers cluster management, including upgrade cycles
- Per-tenant namespace isolation (k8s) or per-tenant task definitions (cloud)
- Billing meters: custom instrumentation to count compute per tenant, per task type
- Audit logging: schema design, ingestion pipeline, query interface, retention management
- Warm pool management: pre-warmed containers to avoid cold starts
- Egress controls: per-pod or per-task network policies
- Secrets management: per-tenant credentials, rotation, injection
- Health monitoring: custom alerting on stuck agents, noisy tenants, failed tasks
- CLAUDE.md injection: a custom agent launcher that writes the right context before the agent starts
- SOC 2 evidence: quarterly evidence collection, auditor liaison, control documentation
Each item is 1–4 weeks of engineering. The list has ~10 items. That is the 4–6 month estimate in the table above, and why it requires a dedicated platform engineer rather than a product engineer splitting their time.
Six-step quickstart
From zero to a running multi-tenant AI SaaS with real customer isolation:
Map your tenant model
Decide: one workspace per customer. Define your pricing tiers (free, pro, enterprise) and what agent compute each tier includes. Write this down before touching any code — it shapes every API call.
Create a workspace at customer signup
Add a POST /api/workspaces call to your customer signup handler. Pass the customer name, plan tier, monthly cost limit, and your internal customer ID as metadata. Store the workspace_id in your database against the customer record.
workspace = sandbox_api.post("/api/workspaces", json={ "name": customer_name, "plan": plan, "cost_limit_monthly": PLAN_LIMITS[plan], "max_concurrent_sandboxes": PLAN_CONFIG[plan]["max_concurrent"], "metadata": {"your_customer_id": customer_id}, }) db.customers.update(customer_id, workspace_id=workspace["id"])
Author your base CLAUDE.md template
Write the core instructions for your product's agents: the persona, the standard operating procedures, the tools available, the escalation protocol. Keep tenant-specific context in a separate section that gets injected per-tenant at launch time.
Build the per-tenant context injector
Write a function that takes a tenant record from your database and returns a CLAUDE.md string — the base template plus the tenant's specific context. This function runs at every sandbox launch, so keep it fast (no remote calls; everything from your DB).
Add the egress allowlist to every launch
Never launch a tenant sandbox without an egress policy. Default to deny-all, then explicitly allow the LLM provider, your product API, and the tenant's authorized integrations. Make the allowlist configurable per workspace so enterprise tenants can have broader access than free-tier tenants.
Wire per-tenant billing
On your invoicing cycle (monthly or metered), call GET /api/workspaces/{workspace_id}/billing for each active workspace. The response includes compute hours, LLM tokens, and total cost in USD. Feed this into your billing provider (Stripe, Chargebee) to generate the customer invoice or trigger the usage-based charge.
Workspace creation at signup, per-tenant CLAUDE.md injection, egress allowlists, and billing attribution. A focused engineer can ship all six steps in a single sprint. The rest of this guide covers the production-hardening layers — compliance streaming, health monitoring, tier-based throttling, churn offboarding — that you add over the following sprints as your customer base grows.
When NOT to use this pattern
- You're an internal-only tool. No customers, no multi-tenancy. Use a single workspace. The workspace abstraction is still useful (gives you isolated billing and audit for your own team), but you don't need the per-customer provisioning flow.
- You have one really big customer. Single-tenant deployment, possibly in their own AWS account, possibly on-prem. This is a different architecture: you deploy an entire instance of your product for them rather than giving them one workspace in a shared platform. We support this on enterprise plans.
- Your SaaS doesn't need agent compute. If your product is purely model-API + your-DB — no file I/O, no browser, no long-running processes — you don't need sandboxes. The model API + your application server is sufficient. Save the platform cost.
- Compliance regime requires you to own the runtime. Some FedRAMP / IL5 deployments require you to control every layer of the stack. We'll be on FedRAMP Moderate by Q3 2026; if you need it sooner, build it yourself on GovCloud. We can consult on the architecture.
- You have one highly unusual technical requirement. If your agents need exotic hardware (custom ASICs, on-prem GPU clusters, real-time sensor access), the platform may not fit. Check with us before building — we extend the platform's engine layer faster than you might expect.
Case study: a vertical-AI startup's first 90 days
Anonymized story of a real customer: vertical AI for finance ops at small accounting firms. The founder had a SaaS background, one year of runway, and no platform-engineering experience.
The problem they were solving
Small accounting firms (2–12 staff) spend 40–60% of junior staff time on three tasks: accounts payable processing, bank reconciliation, and expense categorization. These tasks are high-volume, low-judgment, and well-suited to AI agents. The founder's insight: if each accounting firm's agents run with their specific chart of accounts, their specific vendor list, their specific bank feeds — and nothing from any other firm — the accuracy would be good enough to put the product in production.
The multi-tenancy requirement was not a technical afterthought — it was the core product insight. A shared context would mean an agent trained partly on Firm A's data helping Firm B. Unacceptable. Each firm's agents needed to be entirely isolated.
The build
- Day 1–7: Founder signs up to Sandbox Platform. Builds the onboarding flow: customer signup creates a workspace + 3 default sandboxes (AP clerk, expense auditor, bank-reconciler). Writes the base CLAUDE.md for each agent type. The per-tenant context block pulls from the firm's QBO/Xero connection at first launch.
- Day 8–21: Customer-facing UI — primarily Slack-based (accountants live in Slack) with a web dashboard for admin tasks and audit review. Integrates QuickBooks Online and Xero via OAuth; credentials stored per workspace, never shared across firms.
- Day 22–30: First 5 design-partner firms onboarded. Each firm has its own workspace, its own billing line, its own isolated agent compute. The founder validates that Firm A's agent cannot see Firm B's vendor names or transaction history — the isolation is structural, not just an application-layer filter.
- Day 31–60: Iterating on CLAUDE.md based on partner feedback. Discovering that "bookkeeping accuracy" varies dramatically by whether the CLAUDE.md includes the firm's specific chart of accounts (400-line CSV exported from QBO). Adds a chart-of-accounts ingestion step to onboarding that becomes the single biggest accuracy improvement in the product's history.
- Day 61–90: 30 paying firms. MRR at $18K. Founder raises a seed round on the back of the early traction. Hires first two engineers — neither is a platform engineer; both are product engineers focused on the accounting-domain AI quality and the customer-facing UI.
The numbers
| Metric | Day 30 | Day 60 | Day 90 |
|---|---|---|---|
| Paying customers (firms) | 5 | 18 | 30 |
| Active agent sandboxes | 15 | 54 | 90 |
| Monthly platform cost | $320 | $1,100 | $1,800 |
| Monthly revenue | $2,500 | $9,000 | $18,000 |
| Platform cost as % of revenue | 12.8% | 12.2% | 10% |
| Platform engineers employed | 0 | 0 | 0 |
Total spent on infrastructure in 90 days: ~$3,200 (platform fees + model costs at usage; the model costs were the larger component). The platform cost line item tracked downward as a percentage of revenue as the customer base grew — the per-sandbox cost is largely fixed while the customer's willingness to pay is based on value, not compute.
The founder's reflection at day 90: "The multi-tenant isolation was the product. If we'd built on shared infrastructure to save three weeks of setup, we would have had to rewrite the whole thing before we could sign our first enterprise customer. The workspace-per-firm model is what let us walk into enterprise sales conversations and say 'your data never touches another firm's context' and mean it technically, not just contractually."
FAQs
Can I white-label the dashboard?
Yes — partial today (custom domain, your logo, your colors), full white-label on enterprise tier. Most SaaS founders use API-only access and build their own UI, which gives complete brand control.
What about my customers' data?
It lives in their workspace. You cannot see it without explicit cross-tenant permissions. They own it; they can export it; we delete it on their request per GDPR/CCPA.
How do I handle customer-specific compliance — HIPAA, SOC 2, GDPR?
Per-workspace compliance posture. Some workspaces can be HIPAA-tier (BAA + restricted models); others standard. Configure per workspace at provisioning. The platform holds SOC 2 Type II; request the report via your account dashboard.
What if a customer wants their data in their own AWS account?
Bring-your-own-cloud is on the roadmap for Q3 2026. For now, we run all workspaces in our managed AWS; data residency is configurable by region (us-east-1, eu-west-1, ap-southeast-2).
Can I run my own model layer instead of Anthropic or OpenAI?
Yes — the platform supports custom model endpoints, including self-hosted (Ollama, vLLM, locally-deployed Llama) and bring-your-own-key for any provider including Anthropic, OpenAI, Google Gemini, and Mistral.
How does per-tenant CLAUDE.md work?
You maintain a base CLAUDE.md template for your product. At sandbox launch time, you inject tenant-specific context — their data schema, their integrations, their business rules — via the claude_md parameter in the launch API. Each tenant's agents work with their context from the first message, with no warmup conversation needed.
Can one noisy tenant's agents affect other tenants?
No. Each tenant's sandboxes run in isolated containers with their own compute allocation. CPU and memory are not shared. Network egress is controlled per sandbox. A runaway agent in Tenant A cannot consume Tenant B's resources or network bandwidth.
How do I detect when a tenant is consuming too many resources?
The monitoring API exposes per-workspace and per-sandbox CPU, memory, and cost metrics. Set up webhook alerts when a workspace exceeds a cost or CPU threshold. Apply throttling by reducing the max_concurrent_sandboxes limit for that workspace via a PATCH call — takes effect immediately without restarting running sandboxes.
What does it cost to run 100 tenants?
Compute cost scales with active sandboxes, not with tenant count. 100 tenants, each with 1 active agent on a ab0t.medium running 4 hours per day, costs roughly $0.32 per tenant per day — about $32/day total across all 100 tenants. Idle tenants cost nothing. You can price at $1–5 per active agent-day and maintain 3–15x margin on compute alone.
How do I push a CLAUDE.md update to all tenants simultaneously?
Maintain a CLAUDE.md version in your database. When you publish a new version, update the stored template for all workspaces. The new CLAUDE.md takes effect on the next sandbox launch for each tenant — no restart of running sandboxes required. Running sessions continue with the CLAUDE.md they were launched with until they terminate naturally.
Is the platform SOC 2 Type II certified?
Yes. The platform holds SOC 2 Type II certification. The report covers encryption at rest (AES-256), encryption in transit (TLS 1.3), access logging, network isolation, and availability controls. Request the full report via your account dashboard under Settings → Compliance.
Can I give enterprise customers access to their own audit logs?
Yes. Configure the workspace's audit_stream_destination to point at the customer's S3 bucket, Datadog org, Splunk HEC endpoint, or any webhook. Audit events stream in near-real-time with tenant context on every record, formatted as JSONL for easy ingestion into any SIEM.
What happens when a customer churns — how do I clean up their data?
Call DELETE /api/workspaces/{workspace_id} with confirm_delete: true. This terminates all running sandboxes, wipes the workspace filesystem, revokes all API keys scoped to that workspace, and emits a deletion audit event. Build this into your churn/cancellation flow so it runs automatically — don't rely on manual cleanup.
How do I set different rate limits for free vs pro vs enterprise tiers?
Pass max_concurrent_sandboxes, max_sandboxes_per_day, and max_compute_hours_per_month when creating or updating a workspace. These map directly to your pricing tiers. Set enterprise limits to 0 (no cap). Limits can be updated at any time with a PATCH call — useful when a customer upgrades their plan mid-cycle.
Can agents in different tenant sandboxes communicate with each other?
Not by default. Cross-tenant communication is blocked at the network layer. If your product legitimately requires agents across tenants to coordinate (for example, a shared marketplace agent that aggregates data from multiple tenants), build that coordination layer in your product API — the product API calls each tenant's sandbox separately. Never wire cross-tenant networking at the sandbox level.
What's next
Build your vertical AI SaaS
2–4 weeks to MVP. Workspace-per-customer, per-tenant CLAUDE.md, egress controls, and billing attribution all ship out of the box.
No platform engineer required. Your first 5 workspaces are free.
Open Dashboard