Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesOnboardingOnboarding — Data Model

Onboarding — Data Model

Onboarding does not own its own table. It composes writes across existing tables. See parent feature docs for column-level detail; this file maps the effect of the wizard on the schema.

Rows created by a successful wizard run

OrderTableRowSource step
1users(if not yet created by webhook) — findOrCreateFromClerk inside OrganizationsService.create.implicit
2Clerk-sidenew organizationorg step → handleFinish
3organizationsnew row with name, type, platform_tier='lite', clerk_organization_id.org step → handleFinish
4membershipsnew row, role='owner', status='active'.org step → handleFinish (in OrganizationsService.create)
5legal_consents3 rows (terms_of_use, privacy_policy, fitness_waiver), context='owner_onboarding'.legal step → handleFinish
6(none required; current wizard sends {})If onboarding-complete had classTypeNameclass_types row. If inviteEmails[]invitations + memberships(pending_invitation).done step → handleFinish

Persistence of in-progress wizard state

  • LocalStorage — key fitkit:setup-wizard-v2, value JSON-serialized OnboardingState<SetupWizardContext> (the currentStepId, flowData, internal OnboardJS fields). TTL 7 days. No server-side copy.
  • No DB row persists wizard progress. The org row exists only after handleFinish runs.

Tour completion

  • Column: users.guided_tour_completed_at (timestamptz).
  • Set by PATCH /users/me { guidedTourCompletedAt: <ISO> }. The dashboard reads this and skips the tour if non-null.

Multi-org isolation pattern

Onboarding writes that bootstrap a new org are all scoped to the org being created. The owner row in memberships is the first credential that lets the caller read/write the new org’s data through the standard requireMembership path on subsequent requests.

Soft-delete vs hard-delete

N/A — no onboarding-owned data. Org soft-delete is not exposed; legal consents are immutable (revocation creates a new row).