Skip to main content

Overview

Executor uses a hierarchical isolation model with Organizations as the billing and membership umbrella, and Workspaces as the unit of execution isolation for tasks, tools, and credentials.

Organizations

Organizations represent a billing entity and membership boundary:
// From schema.ts:90-102
organizations: defineTable({
  workosOrgId: v.optional(v.string()),  // External WorkOS org ID
  slug: v.string(),                      // URL-friendly identifier
  name: v.string(),
  status: organizationStatusValidator,   // "active" | "deleted"
  createdByAccountId: v.optional(v.id("accounts")),
  createdAt: v.number(),
  updatedAt: v.number(),
})
  .index("by_workos_org_id", ["workosOrgId"])
  .index("by_slug", ["slug"])
  .index("by_status_created", ["status", "createdAt"])

Organization Properties

slug
string
required
URL-friendly identifier, globally unique. Used in URLs and API paths.
name
string
required
Display name for the organization.
status
'active' | 'deleted'
required
Organization lifecycle status. Only "active" organizations can be used.
workosOrgId
string
External WorkOS organization ID for SSO and directory sync integration.
createdByAccountId
Id<'accounts'>
Account that created the organization.

Workspaces

Workspaces are the primary unit of isolation. Each workspace belongs to exactly one organization:
// From schema.ts:69-82
workspaces: defineTable({
  organizationId: v.id("organizations"),
  slug: v.string(),                      // Unique within organization
  name: v.string(),
  iconStorageId: v.optional(v.id("_storage")),
  createdByAccountId: v.optional(v.id("accounts")),
  createdAt: v.number(),
  updatedAt: v.number(),
})
  .index("by_organization", ["organizationId"])
  .index("by_organization_created", ["organizationId", "createdAt"])
  .index("by_organization_slug", ["organizationId", "slug"])
  .index("by_slug", ["slug"])  // Global slug resolution

Workspace Properties

slug
string
required
URL-friendly identifier. Must be unique within the organization. Can be globally unique for direct resolution.
name
string
required
Display name for the workspace.
organizationId
Id<'organizations'>
required
Parent organization this workspace belongs to.
iconStorageId
Id<'_storage'>
Optional custom icon stored in Convex storage.
createdByAccountId
Id<'accounts'>
Account that created the workspace.

Isolation Boundaries

Workspaces provide isolation for:
Every task belongs to a workspace:
tasks: defineTable({
  taskId: v.string(),
  workspaceId: v.id("workspaces"),  // Workspace isolation
  accountId: v.optional(v.id("accounts")),
  // ...
})
Tasks cannot access resources from other workspaces.

Organization Membership

Members are linked to organizations with role-based access:
// From schema.ts:111-125
organizationMembers: defineTable({
  organizationId: v.id("organizations"),
  accountId: v.id("accounts"),
  role: orgRoleValidator,               // "owner" | "admin" | "member" | "billing_admin"
  status: orgMemberStatusValidator,     // "active" | "pending" | "removed"
  billable: v.boolean(),                // Counts towards seat billing
  invitedByAccountId: v.optional(v.id("accounts")),
  joinedAt: v.optional(v.number()),
  createdAt: v.number(),
  updatedAt: v.number(),
})
  .index("by_org", ["organizationId"])
  .index("by_org_account", ["organizationId", "accountId"])
  .index("by_org_billable_status", ["organizationId", "billable", "status"])

Organization Roles

owner
role
Full administrative access, can delete organization, manage billing.
admin
role
Administrative access, can manage members, workspaces, and settings.
member
role
Standard access, can execute tasks and view workspace resources.
billing_admin
role
Can manage billing and subscription settings.

Billable Seats

The billable flag controls whether a member counts towards the organization’s seat count:
// From schema.ts:116
billable: v.boolean(),  // Counts towards seat billing
Access Pattern: Count billable active members via by_org_billable_status index:
.withIndex("by_org_billable_status", (q) => 
  q.eq("organizationId", orgId)
   .eq("billable", true)
   .eq("status", "active")
)

Invitations

Organizations can invite users via email:
// From schema.ts:133-148
invites: defineTable({
  organizationId: v.id("organizations"),
  workspaceId: v.optional(v.id("workspaces")),  // Optional workspace assignment
  email: v.string(),
  role: orgRoleValidator,
  status: inviteStatusValidator,  // "pending" | "accepted" | "expired" | "revoked" | "failed"
  providerInviteId: v.optional(v.string()),  // External WorkOS invite ID
  invitedByAccountId: v.id("accounts"),
  expiresAt: v.number(),
  acceptedAt: v.optional(v.number()),
  createdAt: v.number(),
  updatedAt: v.number(),
})
Invites can optionally pre-assign users to a specific workspace. If workspaceId is set, the user joins both the organization and the designated workspace.

Access Patterns

Common workspace and organization queries:

Resolve Workspace by Slug

// Global slug lookup
await ctx.db
  .query("workspaces")
  .withIndex("by_slug", (q) => q.eq("slug", "my-workspace"))
  .unique();

// Organization-scoped slug lookup
await ctx.db
  .query("workspaces")
  .withIndex("by_organization_slug", (q) => 
    q.eq("organizationId", orgId).eq("slug", "my-workspace")
  )
  .unique();

List Workspaces in Organization

// Ordered by creation time (newest first)
await ctx.db
  .query("workspaces")
  .withIndex("by_organization_created", (q) => 
    q.eq("organizationId", orgId)
  )
  .order("desc")
  .collect();

Check Organization Membership

// From policies.ts:241-256
const membership = await ctx.db
  .query("organizationMembers")
  .withIndex("by_org_account", (q) => 
    q.eq("organizationId", orgId).eq("accountId", accountId)
  )
  .unique();

if (!membership || membership.status !== "active") {
  throw new Error("Account is not an active member");
}

Dashboard Workflows

Organization Management

  1. Create organization with name and slug
  2. Invite members via email with role assignment
  3. Manage billing through Stripe integration
  4. View usage metrics across all workspaces

Workspace Management

  1. Create workspace within organization
  2. Configure tool sources (workspace or org-scoped)
  3. Manage credentials (account, workspace, or org-scoped)
  4. Set access policies for tool usage
  5. Monitor tasks and approvals

Multi-Tenancy

Executor’s workspace model supports several tenancy patterns:

Pattern 1: Single Organization, Multiple Workspaces

Organization: Acme Corp
├── Workspace: Production
│   ├── Tool Sources (production APIs)
│   ├── Credentials (production keys)
│   └── Access Policies (require approval)
├── Workspace: Staging
│   ├── Tool Sources (staging APIs)
│   └── Access Policies (auto-approve)
└── Workspace: Development
    └── Access Policies (allow all)
Use for: Environment separation (dev/staging/prod)

Pattern 2: Multiple Organizations

Organization: Customer A
└── Workspace: Customer A Workspace

Organization: Customer B
└── Workspace: Customer B Workspace
Use for: Complete customer isolation in SaaS deployments

Pattern 3: Organization-Scoped Resources

Organization: Acme Corp
├── Tool Source: Shared GitHub (org-scoped)
├── Credential: Shared API Key (org-scoped)
├── Workspace: Team A
│   └── Inherits org-scoped resources
└── Workspace: Team B
    └── Inherits org-scoped resources
Use for: Sharing common tools across teams

Best Practices

Workspace Design

  • Create separate workspaces for different environments (dev/staging/prod)
  • Use organization-scoped resources for shared tools and credentials
  • Name workspaces clearly to reflect their purpose

Organization Structure

  • One organization per company/customer in SaaS deployments
  • Use workspace-scoped credentials for sensitive environment-specific keys
  • Assign appropriate roles to members based on responsibilities

Access Control

  • Set restrictive policies in production workspaces
  • Use approval workflows for destructive operations
  • Grant admin role sparingly, use member role for most users
  • Tasks - Task execution within workspaces
  • Tools - Workspace and organization-scoped tool sources
  • Credentials - Multi-scope credential management
  • Policies - Workspace access policies

Build docs developers (and LLMs) love