Developer reference: first-time onboarding (profile → workspace → invite)
This document covers the post-auth onboarding wizard: three UI routes, the User.onboardingStep state machine (0 → 1 → 2 → 3), and the /api/auth/onboarding/* endpoints. It does not cover general account settings (PATCH /api/auth/profile), additional workspace creation after onboarding (see create-workspace — POST /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
| Concern | Location |
|---|---|
| Routes | gaicc-app/Clients/src/App.tsx — /onboarding/profile, /onboarding/workspace, /onboarding/invite wrapped in RequireOnboarding |
| Step enforcement | gaicc-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 shortcut | In RequireOnboarding, if user.hubProvisioned && step < 1, step is treated as 1 so Hub users skip profile (step 0) and land on workspace |
| Public entry redirect | gaicc-app/Clients/src/presentation/components/PublicOnlyRoute.tsx — same ONBOARDING_ROUTES / hubProvisioned logic when a token already exists |
UI pages
| Step index | Route | Component |
|---|---|---|
0 | /onboarding/profile | gaicc-app/Clients/src/presentation/pages/onboarding/ProfileSetup.tsx |
1 | /onboarding/workspace | gaicc-app/Clients/src/presentation/pages/onboarding/WorkspaceSetup.tsx |
2 | /onboarding/invite | gaicc-app/Clients/src/presentation/pages/onboarding/InviteTeam.tsx |
Client navigation after success
ProfileSetup→navigate("/onboarding/workspace")on profile mutation success.WorkspaceSetup→navigate("/onboarding/invite")on workspace mutation success.InviteTeam→navigate("/dashboard")on batch invite success or skip success.
onboardingStep and server effects
| Step | Meaning (approx.) | Advanced by |
|---|---|---|
0 | Profile not completed | registerAdmin creates user with onboardingStep: 0 (auth.service.ts); updateOnboardingProfile sets 1 |
1 | Workspace not completed | setupOnboardingWorkspace sets 2 and clears email verification fields |
2 | Invites not completed / skipped | batchInviteTeam or skipOnboardingInvites sets 3 |
3 | Onboarding complete | RequireOnboarding sends user to /dashboard |
Email verification (workspace step)
- On
registerAdmin, a 6-digitemailVerifyCodeis stored withemailVerifyCodeSentAt, and a verification email is queued (enqueueEmailOrSendtypeverificationCode). updateOnboardingProfilerefreshesemailVerifyCodeSentAtif a code still exists (extends the validity window for the same code when moving into workspace setup).setupOnboardingWorkspacerequires a non-emptyverificationCodematchinguser.emailVerifyCode; TTL isVERIFY_CODE_TTL_MS= 15 minutes (auth.service.ts). On success, codes are cleared and step becomes2.resendOnboardingVerificationCodeonly allowed whenonboardingStep === 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.
| Method | Path | Middleware highlights | Controller |
|---|---|---|---|
PATCH | /auth/onboarding/profile | authenticateJWT, enforceMutationBillingCompliance, uploadImage (multipart) | updateOnboardingProfile |
POST | /auth/onboarding/resend-verification-code | authenticateJWT, enforceMutationBillingCompliance | resendOnboardingVerificationCode |
PATCH | /auth/onboarding/workspace | authenticateJWT, enforceMutationBillingCompliance, uploadImage | setupOnboardingWorkspace |
POST | /auth/onboarding/invite-team | authenticateJWT, enforceMutationBillingCompliance | batchInviteTeam |
POST | /auth/onboarding/skip-invites | authenticateJWT, enforceMutationBillingCompliance | skipOnboardingInvites |
Controller — gaicc-app/Servers/src/controllers/auth.controller.ts
updateOnboardingProfile: JSON-ish body fields:name(trimmed, required). Optional file: uploaded to S3 underavatars/{orgId}/..., key passed to service asavatarS3Key. ReturnsbuildAuthResponseJSON.setupOnboardingWorkspace: ZodworkspaceSchema:organizationName,slug, optionalverificationCode. Optional logo file → S3logos/{orgId}/.... Service enforces code match, TTL, slug uniqueness vs other orgs, andactiveOrganizationIdpresence.batchInviteTeam: Zod:invitesarray length 1–3, each{ email, role }withroleenumAdmin|Editor|Reviewer|Auditor. Service callsinviteUserper row viaPromise.allSettled(partial invite failures do not throw the whole batch; step still advances to3).skipOnboardingInvites: No body; setsonboardingStep: 3.
Service — gaicc-app/Servers/src/services/auth.service.ts
updateOnboardingProfile,setupOnboardingWorkspace,resendOnboardingVerificationCode,batchInviteTeam,skipOnboardingInvitesas above.- Errors use
errno-stylecodevalues consumed byhandleServiceError(for exampleCONFLICTfor slug taken,UNAUTHORIZED/FORBIDDEN/VALIDATIONfor code and step rules).
Client HTTP layer
| Concern | Location |
|---|---|
| Profile / workspace | gaicc-app/Clients/src/infrastructure/repositories/auth.repository.ts — FormData: field name, optional file field file for both PATCH endpoints |
| Workspace DTO | formData: organizationName, slug, optional verificationCode, optional file |
| Invite / skip / resend | Same repository — JSON POST bodies where applicable; resend uses empty POST body |
| React Query hooks | gaicc-app/Clients/src/application/queries/auth.queries.ts — useUpdateOnboardingProfile, 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/profileupdates displaynameonly for the signed-in user and does not changeonboardingStep(seeupdateAccountProfileinauth.service.ts).- Onboarding profile is
PATCH /api/auth/onboarding/profileand advances onboarding to step1.