Workspace Management

chevron-rightRelevant source fileshashtag

Purpose and Scope

This document describes how workspaces are resolved, provisioned, and accessed in Automatos AI. A workspace is the primary multi-tenancy boundary that isolates users' agents, workflows, recipes, and data. Every authenticated request must be scoped to a workspace.

For information about authentication mechanisms (Clerk JWT, API keys, anonymous fallback), see Authentication Flow. For details on how workspace-scoped queries enforce data isolation, see Data Isolation.

Sources: orchestrator/core/auth/hybrid.py


Workspace Resolution

The backend resolves the workspace for each request using a priority waterfall. The _get_workspace_id_from_request function checks multiple sources in order, returning the first valid UUID found.

Resolution Priority

spinner

Sources: orchestrator/core/auth/hybrid.py:29-68

Resolution Functions

Function
Purpose
Returns

_get_workspace_id_from_request()

Extracts workspace_id from request using priority waterfall

Optional[UUID]

_parse_uuid()

Safely parses string to UUID, returns None on failure

Optional[UUID]

_workspace_exists()

Validates workspace exists and is active in database

bool

The _parse_uuid function handles malformed UUIDs gracefully, stripping whitespace and catching exceptions:

Sources: orchestrator/core/auth/hybrid.py:20-27, orchestrator/core/auth/hybrid.py:71-81


Access Verification

When a client explicitly provides a workspace ID via header or query parameter, the backend verifies the user has access to that workspace. This prevents workspace spoofing attacks where a user attempts to access another workspace's data by manipulating request headers.

spinner

The SQL query joins three tables to verify access:

Access is granted if the user either owns the workspace OR is an active member.

Sources: orchestrator/core/auth/hybrid.py:84-107


Auto-Provisioning for New Users

When a user authenticates via Clerk for the first time, the system automatically provisions a complete workspace structure. This happens atomically in _provision_new_user_workspace.

Provisioning Flow

spinner

Database Records Created

Table
Fields
Values

users

clerk_user_id, email, username, name, is_active

From Clerk claims, is_active=true

workspaces

id, name, slug, owner_id, is_personal, plan, plan_limits

UUID, "User's Workspace", unique slug, user.id, true, "starter", default limits JSON

workspace_members

workspace_id, user_id, role, is_active

workspace.id, user.id, "owner", true

Slug Collision Handling

The provisioning function generates a workspace slug from the user's email. If a slug collision occurs (rare but possible), it retries up to 5 times with a random suffix:

Sources: orchestrator/core/auth/hybrid.py:110-187


Personal vs Organization Workspaces

The system supports two types of workspaces:

Personal Workspaces

  • Created automatically for each new user

  • is_personal=true

  • owner_id points to the user

  • User has full control

Organization Workspaces

  • Created when users belong to Clerk organizations

  • clerk_org_id links to Clerk organization

  • is_personal=false

  • Multiple members via workspace_members table

  • Resolved via _resolve_workspace_for_clerk_user

Resolution Logic

spinner

The resolution prioritizes organization workspaces over personal workspaces. If a user belongs to multiple workspaces, the frontend sends x-workspace-id to specify which one.

Sources: orchestrator/core/auth/hybrid.py:190-254


Request Context

The get_request_context_hybrid dependency constructs a RequestContext object for every API request. This context contains the resolved workspace ID and user information.

Context Construction

spinner

RequestContext Structure

All API routers inject this context via Depends(get_request_context_hybrid), ensuring every request has a valid workspace scope.

Sources: orchestrator/core/auth/hybrid.py:283-399, orchestrator/core/auth/dependencies.py


Frontend Integration

The frontend maintains workspace context through the WorkspaceProvider component and consumes the /api/workspaces/current endpoint.

Current Workspace Endpoint

The GET /api/workspaces/current endpoint returns workspace metadata and detects new workspaces for onboarding:

spinner

Sources: orchestrator/api/workspaces.py:24-54

Response Schema


New Workspace Detection

The system detects new workspaces by checking if the workspace has any agents. This triggers the onboarding flow in the frontend.

Onboarding Flow

spinner

The FirstLoginGuard component coordinates this detection:

Sources: frontend/components/onboarding/first-login-guard.tsx:1-35, frontend/lib/shepherd/tour-storage.ts:10-16


Summary Tables

Key Functions Reference

Function
Location
Purpose

_get_workspace_id_from_request

Extracts workspace_id from request headers/query/env

_parse_uuid

Safely parses string to UUID

_workspace_exists

Validates workspace exists and is active

_user_has_workspace_access

Verifies user has access to workspace

_provision_new_user_workspace

Auto-provisions workspace for new Clerk users

_resolve_workspace_for_clerk_user

Resolves workspace from Clerk user/org info

get_request_context_hybrid

Main auth dependency, constructs RequestContext

Workspace Resolution Sources

Priority
Source
Example

1

request.state.workspace_id

Set by middleware

2

x-workspace-id header

Frontend sets this explicitly

3

x-workspace header

Alternative header name

4

workspace_id query param

?workspace_id=uuid

5

WORKSPACE_ID env var

Docker/Railway config

6

DEFAULT_WORKSPACE_ID env var

System default fallback

Workspace Types

Type

is_personal

clerk_org_id

Owner

Members

Personal

true

null

User who created account

Only owner initially

Organization

false

Set

Org owner

Multiple via workspace_members

Sources: orchestrator/core/auth/hybrid.py, orchestrator/api/workspaces.py, frontend/components/onboarding/first-login-guard.tsx


Last updated