ArchitectureFor Founders / Engineers35 min readUpdated May 2026

Multi-Tenant AI Agent SaaS

You're building a vertical AI product — Harvey for [your industry], Pilot for [your domain]. Each customer hires AI workers from you; the workers belong to them, on their books, with their data, with their cost limits.

This is the architecture. Org isolation, per-customer billing, the admin dashboard customers self-serve, and the build-vs-buy decision: you can ship in 2-4 weeks on top of Sandbox Platform, or you can spend 6 months and 2 platform engineers building it yourself.

This guide covers: why multi-tenancy for AI agents is harder than for traditional SaaS (shared process risks, prompt injection, data bleed), the three-layer isolation model (process, filesystem, network), per-tenant CLAUDE.md customization, cost attribution for unit economics, compliance audit trails your enterprise customers can actually use, and the operational patterns for running agent fleets across hundreds of tenants without a dedicated platform team.

Quick Answer

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:

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

Your Customer Your Product UI Sandbox Platform API Per-Customer Workspace Each customer = one workspace = isolated sandboxes + own audit + own billing meter

Three layers:

  1. Your product layer. Customer-facing UI, your business logic, your branding, your unique workflows. You own this.
  2. Sandbox Platform layer. Workspace management, sandbox provisioning, billing meters, audit, model integrations. You consume this via API.
  3. 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:

Customer onboarding flow

python
# 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 modelHow you bill
Flat tierYou charge $X/month; you absorb the variability of compute cost (capped by per-workspace limits)
Pass-through usageYou 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:

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:

python — sandbox launch with egress allowlist
# 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:

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:

  1. Base CLAUDE.md — your product's core instructions, the agent persona, the standard operating procedures that apply to all tenants.
  2. 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:

python — per-tenant CLAUDE.md injection at launch
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 typeWhat it containsWhy it matters
Data schemaTable names, field names, data types, relationshipsAgent generates correct queries without hallucinating column names
Tool integrationsWhich APIs are available, authentication patterns, rate limitsAgent knows what it can call without trial-and-error
Business rulesApproval thresholds, jurisdiction-specific rules, workflow gatesAgent applies the right rules for this tenant without cross-tenant contamination
Escalation contactsWho to notify when uncertain, approval-required thresholdsAgent escalates to the right human, not a generic support address
PII and data handlingWhich fields are sensitive, retention policies, export restrictionsAgent handles sensitive data correctly without a separate policy enforcement layer
Tenant personaCompany name, preferred communication style, terminologyAgent 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:

python — sandbox launch with cost attribution tags
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:

python — pull per-tenant cost for invoicing
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 componentTypical rangeNotes
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 taskHighly variable by task complexity and model
Amortized warm-pool cost$0.001–$0.005 per sessionOnly if you use warm pools for instant startup
Total cost per completed task$0.01–$0.25Median ~$0.05 for a typical agent task

Pricing structures that work for AI agent features:

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

ControlWhat it coversYour responsibility
Encryption at restAll sandbox filesystems, stored files, audit logs — AES-256Manage your own database encryption
Encryption in transitAll API calls, LLM provider calls — TLS 1.3Ensure your product API uses TLS
Access loggingEvery API call, every sandbox action, every file operation — logged with workspace contextDefine who in your team has access to the admin API
Network isolationPer-sandbox egress controls, per-workspace VPC isolationDefine the egress allowlist correctly at launch time
Availability99.9% SLA on the control plane; multi-AZ sandbox provisioningDesign your product for sandbox restart failures
SOC 2 Type IICovers the platform infrastructure, the control plane, and the audit log systemYour 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:

python — configure per-tenant audit stream at workspace creation
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:

python — HIPAA workspace configuration
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?).

python — per-tenant health monitoring
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.

python — rolling CLAUDE.md update across all tenants
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:

python — throttle a noisy tenant
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:

python — tier-based workspace configuration
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

OptionTimelineHeadcountTotal cost (year 1)
Build it yourself in the cloud / k8s4-6 months to MVP, ~12 months to production-ready2 platform engineers$300-450K (salary + AWS)
Build on Sandbox Platform2-4 weeks to MVP1 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:

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:

1

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.

2

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.

python — step 2: create workspace at signup
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"])
3

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.

4

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).

5

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.

6

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.

That is the full MVP.

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

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

The numbers

MetricDay 30Day 60Day 90
Paying customers (firms)51830
Active agent sandboxes155490
Monthly platform cost$320$1,100$1,800
Monthly revenue$2,500$9,000$18,000
Platform cost as % of revenue12.8%12.2%10%
Platform engineers employed000

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