- Sign-up flow that creates a workspace.
- Invite-based team membership with roles (owner / admin / member).
- Per-workspace data isolation enforced at the schema level.
- A workspace switcher in the UI.
- The shape ready to attach billing, projects, or any domain logic.
Prerequisites
What you’ll build
Key invariants:- A user can belong to many workspaces (Carol is in two).
- Within a workspace, a user has exactly one role.
- All domain data is scoped to a workspace.
Step 1 — The foundation prompt
Start a new vly project. Use Plan mode. Submit. Review the plan. Plan mode should produce a schema with the four tables plus indexes, the routes for/onboarding and /w/[slug]/..., the auth middleware that scopes queries, and the workspace switcher component.
Common tweaks at this stage:
- “Slug max length is 32 chars.”
- “Invite link includes the email it was sent to as a query param so the join page can pre-fill.”
- “When the last owner is removed, transfer ownership to the longest-tenured admin instead of blocking.”
Step 2 — Verify the basics
Once the build completes:1
Sign up as user 1
Email + password works. Land on /onboarding.
2
Create a workspace
Name “Acme”, slug “acme”. You become the owner. URL becomes
/w/acme/....3
Open settings, invite a teammate
Use a second email address (or use Resend test-mode and check the Resend dashboard for the email). Set role “admin”.
4
Open the invite link in incognito
Sign up as the second user. The invite is auto-accepted; you’re redirected to
/w/acme/... with admin permissions.5
Verify isolation
Have user 2 create a second workspace (“BetaCorp”). Switch back to user 1’s view — only Acme should be visible.
Sign-up, workspace creation, invites, role assignments, and tenant isolation all work. This is the foundation.
Step 3 — Add domain data
Now layer your actual product. The pattern: every domain table has aworkspaceId, every query filters on the active workspace.
Example — adding “Projects” as your first domain entity:
Now repeat for tasks, comments, files, or whatever your app needs. The base pattern stays the same:
- Add
workspaceIdto the schema. - Index by_workspace.
- Filter by active workspace in every query.
Step 4 — Add billing (optional)
If you’re charging customers, the standard add-on: vly will set up the Stripe integration if needed. See Stripe for setup.Step 5 — SSO (enterprise tier, optional)
If you’ll have B2B customers with strict auth requirements, SSO via SAML / OIDC: See WorkOS for the underlying integration.Going further
Layer any of these on the foundation:Audit logs
Every meaningful action gets an audit log entry, scoped to the workspace.
Webhooks for events
Customers can register webhook endpoints to react to workspace events.
API tokens for customers
Let customers integrate your app via API. Per-workspace tokens, rate-limited.
Multi-region
For latency-sensitive customers, deploy data closer. (Currently roadmap.)
Common mistakes
Forgetting to scope a query
Forgetting to scope a query
Any query that returns workspace data must filter by the active workspace. vly handles this when the agent generates code, but if you hand-write a query in Code mode, don’t forget the filter — that’s how data leaks happen.
Not handling the 'last owner' case
Not handling the 'last owner' case
“Remove member” needs to refuse if the target is the only owner. vly handles this in the prompt above; if you skip it, you can lock yourself out of your own workspace.
Storing plan limits in code instead of DB
Storing plan limits in code instead of DB
“Free tier max 3 projects” — store the limits in a
plans table, not as if (plan === 'free') ... scattered through code. Easier to change.Related
Auth flows
Patterns for the auth scenarios common in multi-tenant SaaS.
Multi-tenancy patterns
Deeper architectural reading.
Subscription billing recipe
Full billing setup.
