Branding and domain setup

MIOSA can run behind your product and your customers’ domains. The important order is: set the organization identity first, create workspaces/projects for downstream customers, configure preview and deployment domains separately, then attach exact custom domains where a customer needs their own hostname.


Setup flow

1. Organization identity

Every account needs a real organization name. MIOSA derives the organization slug from that name, and the slug is permanent after setup. Do this before letting users create resources.

Example:

Organization nameOrganization slugDefault managed deployment URL
Lumenlumenhttps://lead-magnet.lumen.miosa.app

If a newly created account still has a temporary setup slug such as org-... or a slug that does not match the organization name, require setup before normal app navigation.

GET /api/v1/platform/tenants/current
Authorization: Bearer msk_u_...

If setup is required, update the tenant with the final organization name:

PUT /api/v1/tenants/{tenant_id}
Authorization: Bearer msk_u_...
Content-Type: application/json

{
  "name": "Lumen"
}

The API normalizes the slug from name. If a slug is supplied, it must match the normalized organization name. After setup, name and slug are immutable.

2. Workspaces and projects

For white-label platforms, use workspaces for downstream customers and projects for the things they build.

Lumen example:

Organization: Lumen
  Workspace: Acme Corp
    Project: Summer lead magnet
      Sandboxes: generated app previews
      Databases: form submissions
      Deployment: production lead magnet
      Domain: program.acme.com
  Workspace: Northwind Agency
    Project: Booking portal

Create workspaces and projects directly, or let resource create calls auto-resolve them from workspace_slug / project_slug or external_workspace_id / external_project_id.

POST /api/v1/sandboxes
Content-Type: application/json

{
  "template_id": "miosa-sandbox",
  "workspace_slug": "acme-corp",
  "workspace_name": "Acme Corp",
  "project_slug": "summer-lead-magnet",
  "project_name": "Summer Lead Magnet",
  "external_workspace_id": "acct_123",
  "external_user_id": "user-456",
  "external_project_id": "project_789"
}

Responses include workspace_id and project_id. Store those IDs for future calls.

3. URL layers

White-label production has distinct URL layers. Do not mix them:

ResourceDefault URL shape
Sandbox previewhttps://{port}-{sandbox_slug}.sandbox.{preview_domain}
Computer desktophttps://{computer_slug}.computer.miosa.ai/desktop/index.html
Durable deploymenthttps://{deployment_slug}.{deployment_domain}
Deployment fallbackhttps://{deployment_slug}.{organization_slug}.miosa.app
Exact custom domainhttps://customer-owned.example.com

For Lumen, a deployment named Lead Magnet defaults to something like:

https://lead-magnet.lumen.miosa.app

The fallback is always available. If a tenant deployment domain is configured, deployment responses return that branded URL instead. SDKs, CLIs, and frontends should display the returned public_url / url, not construct hostnames.

4. Organization preview domain

Set a tenant preview domain when generated sandbox preview URLs should use your organization’s domain instead of the MIOSA fallback.

Example:

PUT /api/v1/tenant/preview-domain
Content-Type: application/json

{
  "preview_domain": "lumen.app"
}

DNS:

Record typeNameValue
CNAME*proxy.miosa.ai
CNAME*.sandboxproxy.miosa.ai

After DNS propagates:

GET /api/v1/tenant/preview-domain/verify

Sandbox preview URLs then use:

https://5173-sbx01j9x.sandbox.lumen.app

5. Deployment domain

Set a tenant deployment domain when published apps should use your branded production URL base.

Recommended Lumen setup:

*.sandbox.lumen.app  CNAME proxy.miosa.ai
*.apps.lumen.app     CNAME proxy.miosa.ai

Then generated URLs look like:

SurfaceExample
Sandbox previewhttps://5173-sbx01j9x.sandbox.lumen.app
Durable deploymenthttps://lead-magnet.apps.lumen.app
Managed fallbackhttps://lead-magnet.lumen.miosa.app

Current V1 note: public tenant preview-domain routes are exposed. Tenant deployment-domain resolution exists in backend settings/routing; public tenant-level management routes are still operator-managed. Your client code should consume returned deployment public_url values.

6. Resource custom domains

Attach exact domains when a project, deployment, or computer needs its own public URL.

Examples:

  • acme.com for a website deployment
  • program.acme.com for a lead magnet
  • app.acme.com for an app
  • preview.acme.com for a client-specific preview surface

For deployments:

POST /api/v1/deployments/{deployment_id}/domains
Content-Type: application/json

{
  "domain": "program.acme.com"
}

For computers:

POST /api/v1/computers/{computer_id}/domains
Content-Type: application/json

{
  "domain": "desktop.acme.com"
}

The domain response returns DNS instructions and verification status. Exact resource domains take precedence over managed MIOSA URLs.

7. Domain precedence

When MIOSA renders or routes a URL, use this mental model:

PrioritySourceExample
1Exact resource domainprogram.acme.com
2Tenant deployment domainlead-magnet.apps.lumen.app
3MIOSA managed fallbacklead-magnet.lumen.miosa.app

Sandbox preview domains are separate from deployment domains. Computer desktop URLs are separate from both. Exact resource custom domains still win when attached.

8. Branding

Tenant branding controls the product UI, desktop wallpaper, logos, support URL, and whether “Powered by MIOSA” is shown.

PUT /api/v1/tenant/branding
Content-Type: application/json

{
  "custom_app_name": "Lumen",
  "custom_logo_url": "https://lumen.app/logo-dark.png",
  "support_url": "https://help.lumen.app",
  "desktop_wallpaper_url": "https://lumen.app/wallpaper.png",
  "powered_by_visible": false
}

Wallpaper requirements:

PropertyValue
FormatPNG or JPEG
Dimensions1920 x 1080 recommended
File sizeMax 5 MB

9. Browser tokens

Never expose an msk_* API key in a browser. Your backend creates sandboxes, deployments, and domains, then issues scoped browser tokens for the end user.

const token = await client.browserTokens.create({
  workspaceId: "550e8400-e29b-41d4-a716-446655440000",
  projectId: "660e8400-e29b-41d4-a716-446655440001",
  externalWorkspaceId: "acct_123",
  externalUserId: "user-456",
  permissions: ["sandbox:read", "sandbox:exec", "preview:read"],
  ttlSeconds: 3600,
})

Complete setup checklist

  • Require organization setup if the account still has a temporary slug or no organization name.
  • Explain that organization name creates the permanent MIOSA slug one time.
  • Create or auto-resolve workspaces for downstream customers.
  • Create or auto-resolve projects for each app, website, lead magnet, document, or workflow.
  • Store workspace_id and project_id from create responses.
  • Use returned MIOSA fallback URLs until DNS is configured.
  • Configure preview domain if sandbox previews should use your domain.
  • Configure deployment domain if generated apps should use your domain by default.
  • Attach exact custom domains to deployments or computers when a customer wants their own URL.
  • Apply tenant branding and support links.
  • Issue browser tokens from your backend only.

End-to-end walkthrough: per-user provisioning

This walkthrough builds a design-tool SaaS where each customer gets an isolated workspace and never sees the MIOSA brand.

Per-user provisioning

Create one sandbox per customer project. Pass your own IDs so you can look up, filter, and bill these resources later.

Field reference:

FieldPurposeExample
external_user_idYour customer’s stable identifier. Used for usage rollup, quota checks, and filtering."user_42"
external_project_idYour project’s identifier within a customer."proj_lumen_1"
nameURL-safe slug that appears in preview URLs until a custom domain is set."brand-refresh-2026"
metadataArbitrary key-value pairs returned in webhook payloads and API responses.{"source": "lumen"}

Pretty URLs

By default, sandbox previews use *.sandbox.{preview_domain} after you register your preview domain. To give each sandbox a human-readable subdomain, update the slug after creation:

Slugs must be unique within your workspace. If a slug is already taken, MIOSA returns a 409 conflict.

Customize after create

Update sandbox settings at any time without recreating the VM:

Bill by user

Query compute usage keyed to your external_user_id. Use this for per-seat billing, usage dashboards, or chargeback reports.

Per-user quotas

Prevent a single customer from consuming your entire compute budget. Set quotas at the workspace or project level, or enforce them in your own API layer:

Teardown on cancellation

Destroy all sandboxes when a customer cancels their account:

End-user UI components

Give your users a native feel with file browsing, terminal, and run-button components backed by the MIOSA SDK. All three require a browser token with the appropriate scopes (see Preview permission scoping).

File tree

import Miosa from "@miosa/sdk";

const client = new Miosa({ apiKey: browserToken }); // mp_ token from your backend

async function loadFileTree(sandboxId: string, path = "/workspace") {
  const entries = await client.files.list(sandboxId, path);
  return entries.map((e) => ({
    name: e.name,
    type: e.type,   // "file" | "directory"
    path: e.path,
    size: e.size,
  }));
}

Terminal

// Stream terminal output as an async iterable.
const stream = client.exec.stream(sandboxId, "bash", {
  cols: 80,
  rows: 24,
});

for await (const chunk of stream) {
  terminalEmulator.write(chunk.data);
}

Run button

// One-shot exec with streaming output.
const result = await client.exec.run(sandboxId, "npm test", {
  workingDir: "/workspace",
  timeoutMs: 30_000,
});

console.log(result.stdout);
console.log(result.exitCode); // 0 = pass

End-to-end checklist

  • Set branding: product_name, logo_url, support_email
  • Register preview domain and add DNS CNAMEs
  • Verify DNS propagation
  • Register webhook and store the secret in your environment
  • Create sandboxes with external_user_id, external_project_id, and metadata
  • Mint preview tokens server-side only - never expose msk_* to the browser
  • Verify HMAC-SHA256 on every webhook delivery
  • Reject stale webhook timestamps (>5 min)
  • Deduplicate webhook events by event.id
  • Query usage by external_user_id for billing
  • Enforce per-user sandbox quotas before creating
  • Destroy sandboxes on cancellation

See also

Ownership and Attribution

Workspace/project ownership plus external IDs for chargeback and filtering.

Attribution reference →

Custom Domains

Domain APIs, DNS records, and verification responses.

Custom domains →

Workspaces

Workspace create, update, settings, and usage.

Workspaces →

Projects

Project create, update, and workspace scoping.

Projects →

Was this page helpful?