Profile and Onboarding
Overview
This flow decides where a newly authenticated SYSTEM user should go next. It combines live profile data, workspace access, invitation state, and optional password initialization into one deterministic routing sequence.
Prerequisites
Authorization: Bearer <accessToken>X-PORTAL-ACCESS-CODE: <system-portal-code>- secure-channel support for profile or password mutations
- awareness that onboarding data is no longer exposed by a dedicated backend endpoint
Shared Headers
X-PORTAL-ACCESS-CODE: <system-portal-code>
Authorization: Bearer <accessToken>
Content-Type: application/jsonStep-by-Step Flow
1. Fetch the authenticated profile
API endpoint: GET /web/v1/tenant/profile Use this as the source of truth for name, email, locale, and portal metadata.
curl 'https://api.example.com/web/v1/tenant/profile' \
-H 'X-PORTAL-ACCESS-CODE: <system-portal-code>' \
-H 'Authorization: Bearer <accessToken>'{"code":"2000","message":"SUCCESS","data":{"bizId":"ACC_SYS_001","accountEmail":"admin@example.com","accountName":"System Admin","defaultTimezone":"America/New_York"}}2. Update profile fields collected during onboarding
API endpoint: POST /web/v1/tenant/profile/update Use this after the user confirms display name, phone, language, or timezone.
curl -X POST 'https://api.example.com/web/v1/tenant/profile/update' \
-H 'X-PORTAL-ACCESS-CODE: <system-portal-code>' \
-H 'Authorization: Bearer <accessToken>' \
-H 'X-Secure-Channel-Session-Id: <secure-channel-session-id>' \
-H 'Content-Type: application/json' \
-d '{"accountName":"Jane Admin","accountPhone":"+1987654321","defaultLanguage":"en","defaultTimezone":"America/Los_Angeles","defaultCountry":"US"}'{"code":"2000","message":"SUCCESS","data":{"bizId":"ACC_SYS_001","accountName":"Jane Admin","defaultTimezone":"America/Los_Angeles"}}3. Resolve workspace access
API endpoint: GET /web/v1/tenant/workspaces/mine This replaces the removed onboarding aggregate as the source for workspace routing.
curl 'https://api.example.com/web/v1/tenant/workspaces/mine' \
-H 'X-PORTAL-ACCESS-CODE: <system-portal-code>' \
-H 'Authorization: Bearer <accessToken>'{"code":"2000","message":"SUCCESS","data":[{"bizId":"WS_INVITE_TEST_001","workspaceName":"Primary Workspace","workspaceStatus":{"code":"ACTIVE","value":10010701,"name":"ACTIVE"}}]}4. Resolve invitation state separately
API endpoint: GET /web/v1/tenant/profile/invitations Load this when routing depends on pending invites or acceptance actions.
curl 'https://api.example.com/web/v1/tenant/profile/invitations?statuses=10050301' \
-H 'X-PORTAL-ACCESS-CODE: <system-portal-code>' \
-H 'Authorization: Bearer <accessToken>'{"code":"2000","message":"SUCCESS","data":[{"bizId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","workspaceBizId":"WS_ACME_001","invitationStatus":{"code":"PENDING","value":10050301,"label":"Pending"},"canAccept":true}]}5. Initialize a password for first-time users when required
API endpoint: POST /web/v1/tenant/auth/password/init Use this for invited or newly created users who do not yet have a password.
curl -X POST 'https://api.example.com/web/v1/tenant/auth/password/init' \
-H 'X-PORTAL-ACCESS-CODE: <system-portal-code>' \
-H 'X-Client-Hash: <browser-fingerprint>' \
-H 'X-Secure-Channel-Session-Id: <secure-channel-session-id>' \
-H 'Content-Type: application/json' \
-d '{"sessionId":"init-session-xxx","password":"NewP@ssw0rd!"}'{"code":"2000","message":"SUCCESS","data":{"bizId":"ACC_USR_00000001","email":"user@example.com","status":10010202}}Decision Points
- if
workspaces.many, show a selector or use the current default workspace - if
workspaces.emptyandinvitations.pending, route to invitation handling - if both are empty, route to workspace creation
- if the account still has a password-init session, finish that before normal app entry
Error Handling
4010on any step usually means the JWT expired or the portal header is wrong- do not treat the missing
/profile/onboardingendpoint as a backend regression - profile updates should retry only after confirming the secure-channel session is still valid
- if workspace and invitation queries both fail, keep the user in a recovery state instead of guessing the route
Suggested Client Routing
if workspaces.length > 0 -> enter default or show selector
else if invitations.pending.length > 0 -> show invitation page
else -> show create-workspace entrypoint