Data model — Workspace
Tenant container. All resources (finders, locations, maps, sets/categories, tags, custom field definitions) are scoped to a workspace. Free-tier accounts get one workspace; paid tiers can create additional workspaces. Workspace ID is passed as a query parameter on authenticated API requests to scope reads and writes.
Table: workspaces
| Column | Type | Nullable | Description |
|---|---|---|---|
| id | bigint | no | Primary key |
| title | string | no | Workspace display name |
| description | string | yes | Optional description |
| tutorial_enabled | boolean | no | Whether the onboarding tutorial is active for this workspace |
| tutorial_completed_steps | json | yes | Array of step identifiers the user has completed |
| tutorial_skipped_at | timestamp | yes | When the user dismissed the tutorial |
| created_at | timestamp | — | — |
| updated_at | timestamp | — | — |
💡 Tip: The column is
title, notname. Several API responses surface it asname— check the resource/transformer layer if you see a mismatch.
Onboarding tutorial state
tutorial_enabled starts true for new workspaces. The dashboard hides tutorial prompts once all tutorial_completed_steps are recorded or tutorial_skipped_at is set. These columns have no effect on API behavior — they are purely UI hints.
Relations
| Relation | Method | Type | Notes |
|---|---|---|---|
| Users (owners) | owners() | belongsToMany User | via workspace_user; selects only users.id |
| Finders | finders() | belongsToMany Finder | via finder_workspace pivot |
| Maps | maps() | belongsToMany Map | via map_workspace pivot |
| Sets (Categories) | sets() | belongsToMany Set | via set_workspace pivot, with timestamps |
| Locations | locations() | belongsToMany Location | via location_workspace pivot |
| Tags | tags() | hasMany Tag | direct FK on Tag |
| Custom field definitions | customFieldDefinitions() | hasMany CustomFieldDefinition | ordered by sort_order |
⚠️ Warning: Deleting a workspace cascades to all child resources — see API — Workspaces for operational details.
Workspace scoping in the API
Authenticated requests pass ?workspace_id=<id> to scope reads and writes. The backend resolves this parameter via buildWorkspaceAwareEndpoint in the frontend API layer and validates that the user is a member of the requested workspace before allowing access.
Plan limits
The number of workspaces a user can create is gated by subscription tier. Attempting to create a workspace beyond the plan limit returns 403 with a plan upgrade message. Workspace routes are protected by the subscription_tier:free middleware — accounts below the Free tier cannot access workspace endpoints.
The subscription_tier:free middleware is a minimum-tier gate: accounts on free and above pass; accounts on none (or any tier below Free) return 403. Admins and custom-tier accounts always bypass this gate.
Related pages
- API — Workspaces — CRUD and invite endpoints
- Data model: User — workspace membership via
workspace_userpivot - Data model: Finder — workspace-scoped resource
- Data model: Location — workspace-scoped resource
- Workspaces