On a self-hosted deployment with billing disabled, enable enterprise features through environment variables. Then create an organization and attach its workspaces: branding, PII redaction, permission groups, custom blocks, data drains, and organization audit scope depend on the organization that owns a workspace.
This guide covers the standard billing-disabled configuration. If billing is enabled, subscription gates apply and instance-organization variables are ignored.
Enable the feature set
Set both the server flag and its browser twin, then restart the application:
ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=trueThe server flag controls access and the server-resolved workspace settings. The browser twin also enables controls that read browser configuration directly, including SSO on the login page and the Function block's Sandbox selector. On a Compose install or source checkout, bunx sim-setup doctor reports mismatched twins. See Verify your install.
Individual flags override the master switch. For example, to enable the suite with data drains disabled:
ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=true
DATA_DRAINS_ENABLED=false
NEXT_PUBLIC_DATA_DRAINS_ENABLED=falseYou can also leave the master switch unset and enable individual features. The feature flag reference lists them all.
Choose an organization model
One organization for the instance
Use this when everyone on the deployment belongs to the same company. Set the instance organization's name:
INSTANCE_ORG_NAME="Acme Inc"New accounts join it at signup, and their workspaces are created as organization-owned. Optionally set its slug and an existing account to own it:
INSTANCE_ORG_SLUG=acme-inc
INSTANCE_ORG_OWNER_EMAIL=admin@acme.comThe organization is created on the first signup after enabling this mode. If the owner email is unset or does not match an existing account, the account that triggers creation becomes the owner. Transfer ownership later through the Admin API if needed. Creation is idempotent across replicas.
Backfill existing accounts and workspaces
Setting INSTANCE_ORG_NAME does not move existing accounts or workspaces. Run the consolidation script from the repository root. If the organization does not exist yet, provide an owner email that matches an existing Sim account:
# Preview the changes
DATABASE_URL=... \
INSTANCE_ORG_NAME="Acme Inc" \
INSTANCE_ORG_OWNER_EMAIL=admin@acme.com \
bun run apps/sim/scripts/consolidate-users-into-organization.ts
# Apply the same changes
DATABASE_URL=... \
INSTANCE_ORG_NAME="Acme Inc" \
INSTANCE_ORG_OWNER_EMAIL=admin@acme.com \
bun run apps/sim/scripts/consolidate-users-into-organization.ts --applyThe script adds accounts to the organization and attaches their workspaces. It is a dry run unless you pass --apply, and is safe to rerun. Accounts already in another organization are reported and skipped because an account can belong to only one organization. When the instance organization already exists, the script uses its current owner.
Separate organizations managed through the Admin API
Leave INSTANCE_ORG_NAME unset when one deployment serves multiple teams. Create organizations, add members, and attach workspaces through the Admin API.
Generate an admin key once:
openssl rand -hex 32Persist that value as ADMIN_API_KEY in the application's environment, then restart the application. Use the same value in the shell or client issuing the following requests:
ADMIN_API_KEY='<the-same-key-configured-on-the-application>'Generating or assigning a value in the caller's shell does not configure the running application. Requests use the exact x-admin-key header.
Create an organization
The owner account must exist and must not 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"}'Use the returned organization ID as ORG_ID in subsequent commands. Creating the organization does not attach a workspace.
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"}'Attach a workspace
Enterprise settings apply only to workspaces the organization owns. Generate a fresh UUID for each new move. From the repository root:
OPERATION_ID=$(bun -e 'import { generateId } from "@sim/utils/id"; process.stdout.write(generateId())')
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 "{\"operationId\": \"$OPERATION_ID\", \"destinationOrganizationId\": \"$ORG_ID\"}"Keep the same OPERATION_ID when retrying this move; generate a new one only for a new operation. Inspect data.followUpJobs in the response for pending or failed follow-up work. Check it again with:
curl "https://sim.example.com/api/v1/admin/dashboard/workspaces/$WORKSPACE_ID/move-operations/$OPERATION_ID?destinationOrganizationId=$ORG_ID" \
-H "x-admin-key: $ADMIN_API_KEY"Configure organization settings
Use the Settings UI or these API requests:
When invitations are disabled with DISABLE_INVITATIONS and NEXT_PUBLIC_DISABLE_INVITATIONS, use the Admin API to manage organization membership and workspace access. Internal members join the organization. External workspace members receive access to a specific workspace without joining the organization.
Read audit logs and permission groups
Admin-key reads do not require an organization or Enterprise subscription:
# Deployment audit entries, with filters also available in the UI
curl "https://sim.example.com/api/v1/admin/audit-logs?limit=50&workspaceId=$WORKSPACE_ID" \
-H "x-admin-key: $ADMIN_API_KEY"
# One audit entry
curl "https://sim.example.com/api/v1/admin/audit-logs/$AUDIT_LOG_ID" \
-H "x-admin-key: $ADMIN_API_KEY"
# Permission groups, optionally scoped to an organization
curl "https://sim.example.com/api/v1/admin/access-control?organizationId=$ORG_ID" \
-H "x-admin-key: $ADMIN_API_KEY"The organization-scoped /api/v1/audit-logs endpoint uses an API key whose owner is an organization admin or owner. Use the admin endpoint when no organization exists yet. See Audit logs.
Delete an organization
Deleting an organization removes its memberships, invitations, permission groups, and organization settings. Its workspaces are detached and survive. A non-terminal subscription prevents deletion; cancel it first.
Confirm the target by passing its exact slug:
curl -X DELETE "https://sim.example.com/api/v1/admin/organizations/$ORG_ID?confirmSlug=acme-inc" \
-H "x-admin-key: $ADMIN_API_KEY"Schedule the background jobs
Configure a scheduler to call the maintenance endpoints. Generate a cron secret:
openssl rand -hex 32Persist the same value as CRON_SECRET on the application and its scheduler. Each endpoint requires Authorization: Bearer <CRON_SECRET> and returns 401 if the secret is unset or does not match.
| Feature | Endpoint | Suggested schedule | Included in Helm and production Compose schedules |
|---|---|---|---|
| Data drains | GET /api/cron/run-data-drains | Hourly | Yes |
| SCIM reconciliation | GET /api/cron/scim-reconcile | Hourly | Yes |
| Retention: logs | GET /api/logs/cleanup | Daily | No |
| Retention: soft deletes | GET /api/cron/cleanup-soft-deletes | Daily | No |
| Retention: Chat tasks | GET /api/cron/cleanup-tasks | Daily | No |
| Retention: file versions | GET /api/cron/cleanup-file-versions | Daily | No |
| OAuth token cleanup | GET /api/cron/cleanup-oauth-tokens | Hourly | Yes |
Included schedules still require the cron service to be configured and running. For an external scheduler, a request looks like:
curl -H "Authorization: Bearer $CRON_SECRET" \
https://sim.example.com/api/logs/cleanupConfiguring retention windows and enabling deletion are separate actions. Set DATA_RETENTION_ENABLED=true to allow retention cleanup, and schedule the four retention endpoints yourself, for example through Helm's cronjobs.jobs. Neither the flag alone nor the shipped cron schedules expire retained product data. Billing-disabled deployments use only explicitly configured windows, without hosted-plan defaults.
Retention and data-drain work use the configured job backend. Without Trigger.dev, they have in-process execution paths; with Trigger.dev configured, external workers run the jobs. SCIM reconciliation runs directly through its authenticated cron endpoint and does not require Trigger.dev. See Background jobs for operational setup and Data retention for effective policy.
OAuth token cleanup removes expired and revoked credentials independently of sign-in activity. See Sign in with Sim for provider configuration.
Feature flag reference
Explicit individual flags override the master switch, including when set to false. With billing disabled and no enterprise flags set, custom branding, session policies, and the Sim Mailer inbox remain enabled by default. Other features require opt-in.
| Feature | Server variable | Browser variable |
|---|---|---|
| Enterprise suite | 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 |
| Directory provisioning | SCIM_ENABLED | NEXT_PUBLIC_SCIM_ENABLED |
| Custom branding — enabled by default | WHITELABELING_ENABLED | NEXT_PUBLIC_WHITELABELING_ENABLED |
| Audit logs | AUDIT_LOGS_ENABLED | NEXT_PUBLIC_AUDIT_LOGS_ENABLED |
| Insights | USAGE_MONITORING_ENABLED | NEXT_PUBLIC_USAGE_MONITORING_ENABLED |
| Custom blocks | CUSTOM_BLOCKS_ENABLED | NEXT_PUBLIC_CUSTOM_BLOCKS_ENABLED |
| Session policies — enabled by default | SESSION_POLICIES_ENABLED | NEXT_PUBLIC_SESSION_POLICIES_ENABLED |
| Retention deletion | DATA_RETENTION_ENABLED | NEXT_PUBLIC_DATA_RETENTION_ENABLED |
| Data drains | DATA_DRAINS_ENABLED | NEXT_PUBLIC_DATA_DRAINS_ENABLED |
| Credential groups | CREDENTIAL_GROUPS | — |
| Workspace forks | FORKING_ENABLED | NEXT_PUBLIC_FORKING_ENABLED |
| Sim Mailer inbox — enabled by default | INBOX_ENABLED | NEXT_PUBLIC_INBOX_ENABLED |
| Sandboxes | SANDBOXES_ENABLED | NEXT_PUBLIC_SANDBOXES_ENABLED |
Sandbox readiness
A Sandbox entitlement also requires a remote execution provider with credentials and an immutable Function base image. Until the server verifies readiness, the Function block offers no Shell language, and Settings → Sandboxes shows an upgrade notice instead of a sandbox list.
NEXT_PUBLIC_SANDBOXES_ENABLED exposes the Function block's Sandbox selector; it asserts readiness rather than checking it. Set it only after configuring the selected provider and Function base. bunx sim-setup doctor compares it with provider readiness.
JavaScript without import, require, a selected sandbox, or a sandbox file-path reference runs in local Node child workers in the app container when Sandbox flags are off. Python, Shell, JavaScript with external imports, and selected custom Sandboxes require the remote Function base and fail with a configuration error until it is ready. See Sandboxes.
Verify the setup
| Symptom | Check |
|---|---|
| A settings section is missing | Check its server flag and restart the app after changing environment variables. These values load at boot; a rebuild is not needed. |
| The login page has no SSO option, or Function has no Sandbox selector | Set the matching NEXT_PUBLIC_ flag. Run bunx sim-setup doctor to check twins and Sandbox readiness. |
| Settings → Sandboxes shows an upgrade notice | Check Sandbox entitlement, provider credentials, and the immutable Function base. |
| An enabled feature has no effect in a workspace | Confirm that the workspace belongs to an organization. Use the backfill or workspace move procedure if needed. |
| Retention deletes nothing | Check DATA_RETENTION_ENABLED, explicit windows, scheduler requests, and CRON_SECRET. |
To inspect workspace ownership:
SELECT id, name, workspace_mode, organization_id FROM workspace;A workspace with personal mode or a null organization_id does not inherit organization branding, PII redaction, permission groups, or drains.