Workspaces
The unit of billing, audit, and member access.
A workspace is the top-level tenant in Ventus Sessions. Everything else — sessions, API keys, members, audit entries — lives inside one.
Properties
- Slug: lowercase, URL-safe (
acme,acme-staging). Used in dashboard URLs (/ws/<slug>/sessions) and in audit log filters. - Name: human-readable display name. Can be changed; the slug cannot.
- Members: at least one. The creator is the first
owner. See Members for the role matrix. - API keys: zero or more. Each key is scoped to one workspace. See API keys.
- Created_by: the user who created the workspace. Cannot be deleted from the workspace; you can demote them via the Members screen.
Lifecycle
- Created when a signed-in user fills out the onboarding form. The
first workspace for a user is created at /onboarding; subsequent workspaces come from a tenant-admin endpoint that returns 403 for non-admins.
- Active while it has at least one
owner. You cannot remove the
last owner — there's a guard at the API level.
- Suspended is currently not exposed in the dashboard. The backend
supports it (sessions auto-terminate; API keys 401), used by Ventus ops for billing-block scenarios.
- Deleted is hard-delete with a 30-day soft-delete grace. Sessions,
audit log, and members all cascade. Profiles are scrubbed.
Switching workspaces
The top-left workspace switcher lists every workspace where you're a member. Switching changes the URL prefix (/ws/foo/... → /ws/bar/...) and re-issues a workspace-scoped session token under the hood.
If you only have one workspace, the switcher just shows its name.
Why not "organizations" or "teams"?
We considered the org / team / workspace three-tier model that GitHub uses. We deliberately stopped at one tier because:
- Every cross-tenant boundary is one more place PHI can leak.
- Most healthcare orgs operate as one-billing-entity, one-audit-trail.
- Teams within a workspace are achievable via Member roles (viewer /
operator / owner).
If you genuinely need two-level isolation (e.g., a parent group billing multiple subsidiary workspaces), that's a use case we'd support via a parent-workspace SSO + a billing roll-up — not a hardcoded "org" tier. Talk to us.
Common operations
- Create: at
/onboardingfor a first workspace, or viaPOST /v1/dashboard/workspacesfor any signed-in user. The slug must be unique across the entire deployment. - Switch: pick from the top-left dropdown.
- Rename (display name): Workspace settings → Name.
- List my workspaces:
GET /v1/dashboard/workspaces. - Delete: Workspace settings → Delete workspace. Requires typing the slug. 30-day grace; audit retained 7 years for HIPAA.
Where it lives in the data model
workspaces (id, slug, name, created_at, created_by_user_id)
↓
workspace_members (workspace_id, user_id, role)
↓
api_keys (id, workspace_id, masked_prefix, hashed_secret, label, created_at, revoked_at)
↓
sessions (id, workspace_id, created_by_api_key_id, status, …)
↓
admin_audit_log (id, workspace_id, actor_user_id, action, target, created_at)Every row in every table below workspaces carries workspace_id and participates in workspace-scoped Row-Level Security on the Postgres side. That means even a bug in the application layer can't return a session from Workspace B to a request authenticated against Workspace A.