Multi-tenant SaaS
~12 min Python TypeScriptGoal: Embed MIOSA into a multi-tenant product. Every sandbox is tagged with your own workspace and user IDs. Usage rolls up per tenant for billing. End-users interact with their sandboxes directly from the browser without ever seeing your API key.
What you’ll use: Sandboxes, External Attribution, Browser Tokens, Usage API
What you’ll build
An embedded sandbox experience inside your SaaS:
- Your backend creates sandboxes tagged to the right customer.
- Each tenant’s usage is queryable for chargeback.
- Your customer’s browser gets a short-lived token to embed a live preview — no
msk_*key anywhere client-side.
Prerequisites
- A MIOSA workspace API key (
msk_live_*) — see API Keys - Node 22+ or Python 3.11+
Step 1 — Install and configure
Step 2 — Tag every sandbox with your IDs
Pass external_workspace_id, external_user_id, and external_project_id on every sandboxes.create call. MIOSA stores these on the resource row and propagates them to every usage record, audit event, and derived resource (previews, deployments) automatically.
Step 3 — List sandboxes per tenant
Filter the sandbox list by your own IDs to show a customer only their resources, or to enumerate all sandboxes for an account before billing.
Step 4 — Query usage per tenant
Pull a usage report grouped by external_workspace_id at the end of each billing period. Use this to generate invoices or display a cost dashboard inside your product.
Step 5 — Issue browser tokens for direct access
Your customer’s browser needs to embed a live preview. You cannot put msk_* in the frontend. Instead, your backend mints a short-lived share token and returns it to the client.
Backend — mint the token:
Frontend — embed the preview:
<!-- Fetch the token from your backend, then embed the iframe -->
<script>
async function embedPreview(sandboxId) {
const resp = await fetch(`/api/sandboxes/${sandboxId}/preview-token`);
const { shareUrl, expiresAt } = await resp.json();
const iframe = document.getElementById('preview-frame');
iframe.src = shareUrl;
// Re-mint before expiry so the session stays alive
const expiryMs = new Date(expiresAt).getTime() - Date.now() - 60_000;
setTimeout(() => embedPreview(sandboxId), expiryMs);
}
embedPreview('sbx_...');
</script>
<iframe
id="preview-frame"
sandbox="allow-same-origin allow-scripts allow-forms"
style="width: 100%; height: 600px; border: 0;"
></iframe> Step 6 — Full integration wiring
Here is the complete server-side flow for a new-user onboarding request:
What you learned
- Three external ID fields (
external_workspace_id,external_user_id,external_project_id) let you group every MIOSA resource by your own identifiers without any server-side MIOSA configuration. - Usage reports group by the same IDs — direct input to your chargeback logic.
- Share tokens are resource-scoped, expirable, and the only credentials that should reach a browser. The
msk_*key stays on the backend at all times. - Attribution is inherited: a deployment published from a tagged sandbox carries the same external IDs automatically.