Skip to main content

Developer reference: first-time onboarding (profile → workspace → invite)

This document covers the post-auth onboarding wizard: three UI routes, the User.onboardingStep state machine (0123), and the /api/auth/onboarding/* endpoints. It does not cover general account settings (PATCH /api/auth/profile), additional workspace creation after onboarding (see create-workspacePOST /api/workspaces and /create-workspace), or non-onboarding invites (POST /api/users/invite).

Repository layout

  • Web client: gaicc-app/Clients
  • API server: gaicc-app/Servers

Routing and guards

ConcernLocation
Routesgaicc-app/Clients/src/App.tsx/onboarding/profile, /onboarding/workspace, /onboarding/invite wrapped in RequireOnboarding
Step enforcementgaicc-app/Clients/src/presentation/components/RequireOnboarding.tsx — reads auth.user.onboardingStep (default 3); redirects to ONBOARDING_ROUTES[step]; if step >= 3, redirects to /dashboard
Hub shortcutIn RequireOnboarding, if user.hubProvisioned && step < 1, step is treated as 1 so Hub users skip profile (step 0) and land on workspace
Public entry redirectgaicc-app/Clients/src/presentation/components/PublicOnlyRoute.tsx — same ONBOARDING_ROUTES / hubProvisioned logic when a token already exists

UI pages

Step indexRouteComponent
0/onboarding/profilegaicc-app/Clients/src/presentation/pages/onboarding/ProfileSetup.tsx
1/onboarding/workspacegaicc-app/Clients/src/presentation/pages/onboarding/WorkspaceSetup.tsx
2/onboarding/invitegaicc-app/Clients/src/presentation/pages/onboarding/InviteTeam.tsx

Client navigation after success

  • ProfileSetupnavigate("/onboarding/workspace") on profile mutation success.
  • WorkspaceSetupnavigate("/onboarding/invite") on workspace mutation success.
  • InviteTeamnavigate("/dashboard") on batch invite success or skip success.

onboardingStep and server effects

StepMeaning (approx.)Advanced by
0Profile not completedregisterAdmin creates user with onboardingStep: 0 (auth.service.ts); updateOnboardingProfile sets 1
1Workspace not completedsetupOnboardingWorkspace sets 2 and clears email verification fields
2Invites not completed / skippedbatchInviteTeam or skipOnboardingInvites sets 3
3Onboarding completeRequireOnboarding sends user to /dashboard

Email verification (workspace step)

  • On registerAdmin, a 6-digit emailVerifyCode is stored with emailVerifyCodeSentAt, and a verification email is queued (enqueueEmailOrSend type verificationCode).
  • updateOnboardingProfile refreshes emailVerifyCodeSentAt if a code still exists (extends the validity window for the same code when moving into workspace setup).
  • setupOnboardingWorkspace requires a non-empty verificationCode matching user.emailVerifyCode; TTL is VERIFY_CODE_TTL_MS = 15 minutes (auth.service.ts). On success, codes are cleared and step becomes 2.
  • resendOnboardingVerificationCode only allowed when onboardingStep === 1; generates a new code, updates timestamps, sends email; returns { message: "A new code has been sent to your email." }.

API (all under prefix /api as mounted by the server)

Routes are defined in gaicc-app/Servers/src/routes/auth.routes.ts. Unless noted, requests need Authorization: Bearer <JWT> and a resolvable organizationId on the user context where the controller checks req.organizationId.

MethodPathMiddleware highlightsController
PATCH/auth/onboarding/profileauthenticateJWT, enforceMutationBillingCompliance, uploadImage (multipart)updateOnboardingProfile
POST/auth/onboarding/resend-verification-codeauthenticateJWT, enforceMutationBillingComplianceresendOnboardingVerificationCode
PATCH/auth/onboarding/workspaceauthenticateJWT, enforceMutationBillingCompliance, uploadImagesetupOnboardingWorkspace
POST/auth/onboarding/invite-teamauthenticateJWT, enforceMutationBillingCompliancebatchInviteTeam
POST/auth/onboarding/skip-invitesauthenticateJWT, enforceMutationBillingComplianceskipOnboardingInvites

Controllergaicc-app/Servers/src/controllers/auth.controller.ts

  • updateOnboardingProfile: JSON-ish body fields: name (trimmed, required). Optional file: uploaded to S3 under avatars/{orgId}/..., key passed to service as avatarS3Key. Returns buildAuthResponse JSON.
  • setupOnboardingWorkspace: Zod workspaceSchema: organizationName, slug, optional verificationCode. Optional logo file → S3 logos/{orgId}/.... Service enforces code match, TTL, slug uniqueness vs other orgs, and activeOrganizationId presence.
  • batchInviteTeam: Zod: invites array length 1–3, each { email, role } with role enum Admin | Editor | Reviewer | Auditor. Service calls inviteUser per row via Promise.allSettled (partial invite failures do not throw the whole batch; step still advances to 3).
  • skipOnboardingInvites: No body; sets onboardingStep: 3.

Servicegaicc-app/Servers/src/services/auth.service.ts

  • updateOnboardingProfile, setupOnboardingWorkspace, resendOnboardingVerificationCode, batchInviteTeam, skipOnboardingInvites as above.
  • Errors use errno-style code values consumed by handleServiceError (for example CONFLICT for slug taken, UNAUTHORIZED / FORBIDDEN / VALIDATION for code and step rules).

Client HTTP layer

ConcernLocation
Profile / workspacegaicc-app/Clients/src/infrastructure/repositories/auth.repository.tsFormData: field name, optional file field file for both PATCH endpoints
Workspace DTOformData: organizationName, slug, optional verificationCode, optional file
Invite / skip / resendSame repository — JSON POST bodies where applicable; resend uses empty POST body
React Query hooksgaicc-app/Clients/src/application/queries/auth.queries.tsuseUpdateOnboardingProfile, useSetupOnboardingWorkspace, useResendOnboardingVerificationCode, useBatchInviteTeam, useSkipOnboardingInvites

Successful auth responses update Redux via the same patterns as login/register (setAuth with returned token, user, workspaces).


Differences from PATCH /api/auth/profile

  • PATCH /api/auth/profile updates display name only for the signed-in user and does not change onboardingStep (see updateAccountProfile in auth.service.ts).
  • Onboarding profile is PATCH /api/auth/onboarding/profile and advances onboarding to step 1.