+ Comma-separated workspace slugs (e.g. my-org,my-org-dev) or * for all
+ workspaces. New users are automatically added as Members and skip the onboarding flow.
+
diff --git a/apps/api/plane/authentication/utils/user_auth_workflow.py b/apps/api/plane/authentication/utils/user_auth_workflow.py
index 4641f332c5a..8af984a74fb 100644
--- a/apps/api/plane/authentication/utils/user_auth_workflow.py
+++ b/apps/api/plane/authentication/utils/user_auth_workflow.py
@@ -2,8 +2,9 @@
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.
-from .workspace_project_join import process_workspace_project_invitations
+from .workspace_project_join import auto_join_default_workspaces, process_workspace_project_invitations
def post_user_auth_workflow(user, is_signup, request):
process_workspace_project_invitations(user=user)
+ auto_join_default_workspaces(user=user)
diff --git a/apps/api/plane/authentication/utils/workspace_project_join.py b/apps/api/plane/authentication/utils/workspace_project_join.py
index 9222791a845..15a486520c3 100644
--- a/apps/api/plane/authentication/utils/workspace_project_join.py
+++ b/apps/api/plane/authentication/utils/workspace_project_join.py
@@ -2,6 +2,9 @@
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.
+# Python imports
+import os
+
# Django imports
from django.utils import timezone
@@ -9,6 +12,7 @@
from plane.db.models import (
ProjectMember,
ProjectMemberInvite,
+ Workspace,
WorkspaceMember,
WorkspaceMemberInvite,
)
@@ -89,3 +93,65 @@ def process_workspace_project_invitations(user):
# Delete all the invites
workspace_member_invites.delete()
project_member_invites.delete()
+
+
+def auto_join_default_workspaces(user):
+ """
+ If DEFAULT_WORKSPACE_SLUGS is configured and the user has no workspace memberships,
+ automatically add them as a Member to all listed workspaces and mark onboarding
+ complete so they land directly in the first workspace without the onboarding flow.
+
+ DEFAULT_WORKSPACE_SLUGS accepts:
+ - A comma-separated list of workspace slugs: "my-org,my-org-dev"
+ - A wildcard "*" to auto-join all workspaces on the instance
+
+ The first slug (or oldest workspace for "*") becomes the landing workspace.
+ """
+ from plane.license.utils.instance_value import get_configuration_value
+
+ (slugs_raw,) = get_configuration_value(
+ [{"key": "DEFAULT_WORKSPACE_SLUGS", "default": os.environ.get("DEFAULT_WORKSPACE_SLUGS", "")}]
+ )
+ if not slugs_raw:
+ return
+
+ slugs_raw = slugs_raw.strip()
+
+ # Only auto-join users who have no workspace memberships yet
+ if WorkspaceMember.objects.filter(member=user, is_active=True).exists():
+ return
+
+ if slugs_raw == "*":
+ workspaces = list(Workspace.objects.order_by("created_at"))
+ slug_order = {} # not used for wildcard; primary = oldest workspace
+ else:
+ slugs = [s.strip() for s in slugs_raw.split(",") if s.strip()]
+ if not slugs:
+ return
+ workspaces = list(Workspace.objects.filter(slug__in=slugs))
+ slug_order = {s: i for i, s in enumerate(slugs)}
+
+ if not workspaces:
+ return
+
+ WorkspaceMember.objects.bulk_create(
+ [WorkspaceMember(workspace=w, member=user, role=15, is_active=True) for w in workspaces],
+ ignore_conflicts=True,
+ )
+
+ # Primary (landing) workspace: first by slug order, or oldest for wildcard
+ primary = workspaces[0] if slugs_raw == "*" else min(workspaces, key=lambda w: slug_order.get(w.slug, 999))
+
+ # Mark onboarding complete so the user lands directly in the workspace
+ from plane.db.models.user import Profile
+
+ Profile.objects.filter(user=user).update(
+ is_onboarded=True,
+ last_workspace_id=primary.id,
+ onboarding_step={
+ "profile_complete": True,
+ "workspace_create": True,
+ "workspace_invite": True,
+ "workspace_join": True,
+ },
+ )
diff --git a/apps/api/plane/utils/instance_config_variables/core.py b/apps/api/plane/utils/instance_config_variables/core.py
index 274c6539af9..ceab6e00a74 100644
--- a/apps/api/plane/utils/instance_config_variables/core.py
+++ b/apps/api/plane/utils/instance_config_variables/core.py
@@ -33,6 +33,12 @@
"category": "WORKSPACE_MANAGEMENT",
"is_encrypted": False,
},
+ {
+ "key": "DEFAULT_WORKSPACE_SLUGS",
+ "value": os.environ.get("DEFAULT_WORKSPACE_SLUGS", ""),
+ "category": "WORKSPACE_MANAGEMENT",
+ "is_encrypted": False,
+ },
]
google_config_variables = [
diff --git a/packages/types/src/instance/workspace.ts b/packages/types/src/instance/workspace.ts
index 3f4f4853b58..ebcdd5ff4ad 100644
--- a/packages/types/src/instance/workspace.ts
+++ b/packages/types/src/instance/workspace.ts
@@ -4,4 +4,4 @@
* See the LICENSE file for details.
*/
-export type TInstanceWorkspaceConfigurationKeys = "DISABLE_WORKSPACE_CREATION";
+export type TInstanceWorkspaceConfigurationKeys = "DISABLE_WORKSPACE_CREATION" | "DEFAULT_WORKSPACE_SLUGS";