On Sim Cloud, enterprise features are unlocked by an Enterprise subscription. Self-hosted deployments have no subscription, so they are unlocked by environment configuration instead.
There are two parts to getting this right, and skipping the second is the most common reason features appear to do nothing:
- Enable the features with
ENTERPRISE_ENABLED. - Give them an organization to apply to. Whitelabeling, PII redaction, permission groups, data drains, and audit scoping all read their settings from the organization that owns a workspace. A deployment where everyone works in personal workspaces has no organization for those settings to come from.
Enable the feature set
Set the master switch and its client twin. Both are required — the server value decides access, and the NEXT_PUBLIC_ value decides what the settings UI shows.
ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=trueThat turns on organizations, permission groups, SSO, whitelabeling, audit logs, session policies, data retention, data drains, workspace forks, the Sandbox entitlement, and the inbox. Sandboxes remain unavailable until their remote provider and dedicated Function base are configured.
Turning one feature off
Every feature keeps its own flag, and an explicitly set flag always wins over the master switch. To run the suite without data drains:
ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=true
DATA_DRAINS_ENABLED=false
NEXT_PUBLIC_DATA_DRAINS_ENABLED=falseThe individual flags also work on their own if you would rather opt in one at a time and leave the master switch unset.
| Feature | Server variable | Client variable |
|---|---|---|
| Everything below | ENTERPRISE_ENABLED | NEXT_PUBLIC_ENTERPRISE_ENABLED |
| Organizations | ORGANIZATIONS_ENABLED | NEXT_PUBLIC_ORGANIZATIONS_ENABLED |
| Permission groups | ACCESS_CONTROL_ENABLED | NEXT_PUBLIC_ACCESS_CONTROL_ENABLED |
| SAML and OIDC sign-in | SSO_ENABLED | NEXT_PUBLIC_SSO_ENABLED |
| Custom branding | WHITELABELING_ENABLED | NEXT_PUBLIC_WHITELABELING_ENABLED |
| Audit logs | AUDIT_LOGS_ENABLED | NEXT_PUBLIC_AUDIT_LOGS_ENABLED |
| Session policies | SESSION_POLICIES_ENABLED | NEXT_PUBLIC_SESSION_POLICIES_ENABLED |
| Data retention deletion | DATA_RETENTION_ENABLED | NEXT_PUBLIC_DATA_RETENTION_ENABLED |
| Data drains | DATA_DRAINS_ENABLED | NEXT_PUBLIC_DATA_DRAINS_ENABLED |
| Workspace forks | FORKING_ENABLED | — |
| Sim Mailer inbox | INBOX_ENABLED | NEXT_PUBLIC_INBOX_ENABLED |
| Sandboxes | SANDBOXES_ENABLED | NEXT_PUBLIC_SANDBOXES_ENABLED |
Sandboxes also need a remote execution provider and a dedicated Function base image. Build and configure that base before enabling the UI; custom workspace sandboxes layer their packages on top of it.
For E2B:
E2B_API_KEY=... \
bun run apps/sim/scripts/build-function-e2b-template.ts \
--name sim-function
SANDBOX_PROVIDER=e2b
E2B_ENABLED=true
E2B_API_KEY=...
E2B_FUNCTION_TEMPLATE_ID=<sim-function-template>:<sim-function-build-id>
E2B_FUNCTION_TEMPLATE_GENERATION=<release-epoch-ms>
SANDBOXES_ENABLED=true
NEXT_PUBLIC_SANDBOXES_ENABLED=trueThe builder uses E2B's maintained code-interpreter-v1 base, assigns a fresh
release generation, and prints both runtime values. --generation remains
available for release automation, and --base-template accepts an immutable
base override when a deployment deliberately owns one.
For Daytona, use the immutable snapshot ID printed by the builder. The API key needs
write:snapshots to build and write:sandboxes to execute:
DAYTONA_API_KEY=... \
bun run apps/sim/scripts/build-function-daytona-snapshot.ts \
--name sim-function-2026-08-03 \
--parity-manifest /tmp/function-sandbox-manifest.json
SANDBOX_PROVIDER=daytona
DAYTONA_API_KEY=...
DAYTONA_FUNCTION_SNAPSHOT_ID=<snapshot-uuid>
SANDBOXES_ENABLED=true
NEXT_PUBLIC_SANDBOXES_ENABLED=trueSANDBOXES_ENABLED grants the server-side self-hosted entitlement.
NEXT_PUBLIC_SANDBOXES_ENABLED projects provider readiness to the browser and
exposes Shell plus custom Sandbox management. Set the public flag only after the
selected provider has credentials and a valid immutable Function base configured.
The Function language value itself is never conditioned on these flags, so a
saved Python block cannot be silently serialized or executed as JavaScript.
JavaScript without import or require does not use this remote provider and
continues to run in the local isolated VM when all Sandbox flags are off. Python,
Shell, JavaScript with external imports, and selected custom Sandboxes fail with
an explicit configuration error until the remote Function base is ready.
Mothership's function_execute and run_code tools use Mothership's separate
shell image, including for JavaScript without imports. If the deployment uses
Mothership code tools, also configure the image produced by the Mothership
release process for the selected provider:
# E2B
MOTHERSHIP_E2B_TEMPLATE_ID=<mothership-shell-template-ref>
# Daytona
DAYTONA_SHELL_SNAPSHOT_ID=<mothership-shell-snapshot-ref>These values are selected only for workflow Copilot and workspace Mothership
code-tool calls. They never replace or act as a fallback for
E2B_FUNCTION_TEMPLATE_ID or
DAYTONA_FUNCTION_SNAPSHOT_ID; Function blocks and custom workspace sandboxes
continue to use the dedicated Function base.
Use E2B as the release baseline before building or promoting Daytona:
# 1. Verify the exact E2B Function build and capture its accepted package/runtime surface.
E2B_ENABLED=true \
E2B_API_KEY=... \
E2B_FUNCTION_TEMPLATE_ID=<sim-function-template>:<sim-function-build-id> \
E2B_FUNCTION_TEMPLATE_GENERATION=<release-epoch-ms> \
SANDBOX_PARITY_MANIFEST_OUT=/tmp/function-sandbox-manifest.json \
bun run apps/sim/scripts/verify-sandbox-parity.ts
# 2. Pin Daytona's reconstructed packages to that accepted E2B manifest.
DAYTONA_API_KEY=... \
bun run apps/sim/scripts/build-function-daytona-snapshot.ts \
--name sim-function-2026-08-03 \
--parity-manifest /tmp/function-sandbox-manifest.json
# 3. Verify the immutable Daytona snapshot against the same baseline before promotion.
SANDBOX_PROVIDER=daytona \
DAYTONA_API_KEY=... \
DAYTONA_FUNCTION_SNAPSHOT_ID=<snapshot-uuid> \
SANDBOX_PARITY_MANIFEST_BASELINE=/tmp/function-sandbox-manifest.json \
bun run apps/sim/scripts/verify-sandbox-parity.tsE2B_FUNCTION_TEMPLATE_ID and DAYTONA_FUNCTION_SNAPSHOT_ID fail closed when
unset or mutable. The E2B value must be an exact <template>:<build-id> ref,
and E2B_FUNCTION_TEMPLATE_GENERATION must be the monotonic value printed by
the same build. The Daytona value must be a snapshot ID rather than a name. Sim does not
fall back to MOTHERSHIP_E2B_TEMPLATE_ID or
DAYTONA_SHELL_SNAPSHOT_ID; Mothership, Function, document, and Pi images have
separate package contracts and release cadences.
Assign every promoted E2B Function base a generation greater than every prior deployment. A rollback is a new promotion and therefore also needs a new, higher generation; do not reuse the generation from the older release.
Data retention is the one feature that deletes data. Its flag controls the cleanup pass, not the settings screen — retention windows are always configurable. Nothing is ever deleted until you enable it, and even then only against windows you configured explicitly. Sim never applies the hosted plan defaults to a self-hosted deployment.
Choose an organization model
Pattern 1: one organization for the whole instance
Best when everyone on the deployment belongs to the same company. Set a name and every user joins that organization automatically at signup, with their workspaces created org-owned.
INSTANCE_ORG_NAME="Acme Inc"Optionally pin the slug and the owner:
INSTANCE_ORG_SLUG=acme-inc
INSTANCE_ORG_OWNER_EMAIL=admin@acme.comThe organization is created the first time a user signs up. If INSTANCE_ORG_OWNER_EMAIL is not set, or names a user who does not exist yet, the first user to sign up becomes the owner; move ownership later with the Admin API. Provisioning is idempotent and safe across multiple replicas.
Instance-organization mode only applies when billing is disabled. With billing enabled, organizations are created through the normal subscription flow and these variables are ignored.
Existing deployments
Users and workspaces created before you set INSTANCE_ORG_NAME stay where they are. Move them across once with the backfill script, which adds every user to the organization and attaches their workspaces:
# Preview
DATABASE_URL=... INSTANCE_ORG_NAME="Acme Inc" \
bun run apps/sim/scripts/consolidate-users-into-organization.ts
# Apply
DATABASE_URL=... INSTANCE_ORG_NAME="Acme Inc" \
bun run apps/sim/scripts/consolidate-users-into-organization.ts --applyIt is a dry run unless you pass --apply, and it is safe to re-run. Users who already belong to a different organization are reported and skipped, since a user can only belong to one.
Pattern 2: many organizations you manage yourself
Best when one deployment serves several teams that should not see each other's data. Leave INSTANCE_ORG_NAME unset and provision organizations through the Admin API.
Set an admin key first:
ADMIN_API_KEY=$(openssl rand -hex 32)Create an organization
The owner must not already belong to another organization.
curl -X POST https://sim.example.com/api/v1/admin/organizations \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Inc", "ownerId": "user_123", "slug": "acme-inc"}'Add members
curl -X POST https://sim.example.com/api/v1/admin/organizations/$ORG_ID/members \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "user_456", "role": "member"}'Move a workspace into the organization
Organization-scoped features only apply to workspaces the organization owns.
curl -X POST https://sim.example.com/api/v1/admin/dashboard/workspaces/$WORKSPACE_ID/move \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"destinationOrganizationId\": \"$ORG_ID\"}"Configure organization settings
Branding, retention, and session policies can be set from the API instead of the UI.
curl -X PATCH https://sim.example.com/api/v1/admin/organizations/$ORG_ID/whitelabel \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"brandName": "Acme AI", "hidePoweredBySim": true}'curl -X PATCH https://sim.example.com/api/v1/admin/organizations/$ORG_ID/data-retention \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"logRetentionHours": 2160}'curl -X PATCH https://sim.example.com/api/v1/admin/organizations/$ORG_ID/session-policy \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"maxSessionHours": 168, "idleTimeoutHours": 48}'Deleting an organization requires echoing its slug, because the delete cascades to members, invitations, and permission groups, and detaches its workspaces:
curl -X DELETE "https://sim.example.com/api/v1/admin/organizations/$ORG_ID?confirmSlug=acme-inc" \
-H "x-admin-key: $ADMIN_API_KEY"Verifying it worked
If a feature is enabled but nothing appears, check these in order.
The settings section is missing. The NEXT_PUBLIC_ twin is not set, or the app was not restarted after adding it. Client variables are read at build and boot.
The section appears but the API returns 403. The server-side variable is missing while its client twin is set. Set both.
The feature is on but has no effect inside a workspace. The workspace is not owned by an organization. Check workspace_mode and organization_id:
SELECT id, name, workspace_mode, organization_id FROM workspace;A workspace showing personal or a null organization_id will not pick up branding, PII redaction, permission groups, or drains. Use the backfill script or the workspace move endpoint.
Retention is configured but nothing is deleted. DATA_RETENTION_ENABLED is unset. Configuring windows and running the cleanup pass are separate switches by design.