Skip to main content

Onboarding Analytics Admin

Author: Josh S. Sakweli, Backend Lead Team
Last Updated: 2025-01-05
Version: v1.0

Base URL: https://api.fursahub.com/api/v1

Short Description: AdminOnboarding flow endpoints for analyzingFursa userHub. demographicsGuides new users through phone verification, preference selection, and onboardingprofile data.completion. ProvidesAll summarycontent statistics,is detailedtranslated breakdownsbased byon preferenceuser's page,preferredLanguage and user segmentation capabilities. Data collected here powers recommendations and business insights.setting.

Hints:

  • AllOnboarding endpointssteps requiremust ADMINbe orcompleted SUPER_ADMINin roleorder - skipping ahead returns 412 PRECONDITION_FAILED
  • DemographicsPhone dataverification comesuses fromOTP usersent responsesvia toSMS (supports TZ, KE, UG, RW, BI country codes)
  • Preference pages are dynamic - admin can add/remove/reorder without code changes
  • User's preferredLanguage determines translation of all onboarding preference pagescontent
  • UseEmail theseverification endpointsis foroptional/skippable adminby dashboards,default reports,(Firebase andhandles recommendationactual engine input
  • Internal service methods available for social media and recommendation featuresverification)

AnalyticsComplete DataOnboarding Flow

┌─────────────────────────────────────────────────────────────────────────┐
│                        ANALYTICSCOMPLETE DATAONBOARDING FLOW                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  DATAAfter COLLECTIONPOST (During/auth/firebase/authenticate:                                Onboarding):│
│  └── Check response.data.onboarding.currentStep                         │
│                                                                          │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ UserSTEP completes1: preferencePENDING_EMAIL_VERIFICATION pages:(Optional/Skippable)         │    │
│  ├─────────────────────────────────────────────────────────────────┤    │
│  │ • Firebase handles actual email verification                     │    │
│  │ • "interests"Check: GET ["jobs", "funding", "events"]/onboarding/email-verification/status              │    │
│  │ • "goals"Skip: POST ["find_job", "start_business"]/onboarding/email-verification/skip                │    │
│  │ • "experience"Or wait ["student",for "0-2_years"]user to verify email in Firebase                  │    │
│  │  Auto-transitions when Firebase Storedreports in:email user_onboarding_response tableverified          │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                              ↓                                           │
│  DATA ANALYSIS (Admin Dashboard):                                        │
│  ────────────────────────────────────────────────────────────────┐    │
│  │ GETSTEP 2: PENDING_PHONE_VERIFICATION (Required)                   │    │
│  ├─────────────────────────────────────────────────────────────────┤    │
│  │ • POST /onboarding/analytics/summaryauth-phone/request-otp                       │    │
│  │   └── User enters phone number (+255...)                        │    │
│  │   └── Backend sends OTP via SMS                                 │    │
│  │   └── Returns token for verification                            │    │
│  │ • TotalPOST users,/onboarding/auth-phone/verify                            completion    rates│
│  │   └── User enters 6-digit OTP                                   │    │
│  │   └── On success: phone saved, transitions to next step         │    │
│  │ • Breakdown by status, auth provider, language, theme          │     │
│  │                                                                 │     │
│  │ GETPOST /onboarding/analytics/demographicsauth-phone/resend-otp (if │
│  │ • Per-page option counts and percentages                       │     │
│  │ • "60% of users interested in jobs"                            │     │
│  │ • "45% want to start a business"needed)            │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                              ↓                                           │
│  DATA USAGE (Internal Systems):                                          │
│  ────────────────────────────────────────────────────────────────┐    │
│  │ RecommendationSTEP Engine:3: PENDING_PREFERENCES (Required)                          │    │
│  ├─────────────────────────────────────────────────────────────────┤    │
│  │ • GET /onboarding/pages?current=true                            │    │
│  │   └── Get current preference page                               │    │
│  │ • getUserSelections(userId,Loop "interests")through pages:                                           │    │
│  │   └── Display options (translated to user's language)           │    │
│  │   └── User selects options                                      │    │
│  │   └── POST /onboarding/pages/{pageId}/response                  │    │
│  │   └── Or POST /onboarding/pages/{pageId}/skip (if skippable)    │    │
│  │   └── Move to next page until all complete                      │    │
│  │ • getUsersWithSimilarPreferences(userId)Auto-transitions when all pages completed                     │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                              ↓                                           │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │ STEP 4: PENDING_PROFILE_COMPLETION (Required)                   │    │
│  ├─────────────────────────────────────────────────────────────────┤    │
│  │ • GET /profile                                                  │    │
│  │   └── Get current profile (some data from Firebase)             │    │
│  │ • ShowPUT relevant opportunities based on user interests/profile                                                  │    │
│  │   └── fullName (required)                                       │    │
│  │   Content└── Personalization:username (required, unique)                               │    │
│  │   └── bio (required)                                            │    │
│  │   └── gender, link (optional)                                   │    │
│  │ • On save: auto-completes onboarding                            │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                              ↓                                           │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │ STEP 5: COMPLETED ✓                                             │    │
│  ├─────────────────────────────────────────────────────────────────┤    │
│  │ • Navigate to home screen                                       │    │
│  │ • User interestedcan innow "funding"access all Showapp grants first             │     │
│  │ • User goal is "learn_skills" → Prioritize courses             │     │
│  │                                                                 │     │
│  │ User Segmentation:                                              │     │
│  │ • getUsersBySelection("goals", "start_business")               │     │
│  │ • Target campaigns to specific user groupsfeatures                          │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Phone Verification Endpoints


1. GetRequest Summary StatisticsOTP

Purpose: GetSend high-levelOTP onboardingcode statisticsto user's phone number for admin dashboard.verification.

Endpoint: GETPOST {base_url}/onboarding/analytics/summaryauth-phone/request-otp

Access Level: 🔒 Protected (Admin Only)

Authentication: Bearer Token

(ROLE_ADMIN

Prerequisite: orUser ROLE_SUPER_ADMIN)must be at PENDING_PHONE_VERIFICATION step

Request HeadersJSON Sample:

{
  "phoneNumber": "+255712345678"
}

Request Body Parameters:

HeaderParameter Type RequiredDescriptionValidation
phoneNumberstringYesPhone number with country codeE.164 format: +255XXXXXXXXX

Supported Country Codes:

  • +255 - Tanzania
  • +254 - Kenya
  • +256 - Uganda
  • +250 - Rwanda
  • +257 - Burundi

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP sent successfully",
  "action_time": "2025-01-05T10:30:45",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "phoneNumber": "+255****678",
    "expiresInSeconds": 600,
    "resendAvailableIn": 120
  }
}

Success Response Fields:

Field Description
AuthorizationtokenTemporary token for verify/resend endpoints
phoneNumberMasked phone number for display
expiresInSecondsOTP validity period (10 minutes)
resendAvailableInSeconds until resend allowed (2 minutes)

Error Responses:

Wrong Onboarding Step (412):

{
  "success": false,
  "httpStatus": "PRECONDITION_FAILED",
  "message": "Onboarding step required",
  "action_time": "2025-01-05T10:30:45",
  "data": {
    "message": "Complete email verification first",
    "currentStep": "PENDING_EMAIL_VERIFICATION",
    "requiredStep": "PENDING_EMAIL_VERIFICATION"
  }
}

Rate Limit Exceeded (429):

{
  "success": false,
  "httpStatus": "TOO_MANY_REQUESTS",
  "message": "Too many OTP requests. Try again in 10 minutes.",
  "action_time": "2025-01-05T10:30:45",
  "data": "Too many OTP requests. Try again in 10 minutes."
}

Phone Already Registered (409):

{
  "success": false,
  "httpStatus": "CONFLICT",
  "message": "Phone number already registered",
  "action_time": "2025-01-05T10:30:45",
  "data": "Phone number already registered"
}

2. Verify OTP

Purpose: Verify OTP code and complete phone verification.

Endpoint: POST {base_url}/onboarding/auth-phone/verify

Access Level: 🔒 Protected

Authentication: Bearer Token

Request JSON Sample:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "otp": "123456"
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
token string Yes BearerToken {accessToken}from request-otp responseMust be valid, non-expired
otpstringYes6-digit OTP codeExactly 6 digits

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "SummaryPhone retrieved"verified successfully",
  "action_time": "2025-01-05T14:00:05T10:35:00",
  "data": {
    "totalUsers"verified": 5000,true,
    "completedOnboarding"phoneNumber": 4200,"+255****678",
    "completionRate"onboardingStatus": 84.0,
    "totalPagesConfigured": 3,
    "byStatus": {
      "COMPLETED": 4200,
      "PENDING_PHONE_VERIFICATION": 300, "PENDING_PREFERENCES": 350,
      "PENDING_PROFILE_COMPLETION": 100,
      "PENDING_EMAIL_VERIFICATION": 50
    },
    "byAuthProvider"nextStep": {
      "GOOGLE": 3500,
      "APPLE": 800,
      "EMAIL": 700
    },
    "byLanguage": {
      "en": 2000,
      "sw": 2500,
      "fr": 400,
      "zh": 100
    },
    "byTheme": {
      "SYSTEM": 3000,
      "DARK": 1200,
      "LIGHT": 800/api/v1/onboarding/pages"
  }
}

Error Responses:

Invalid OTP (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Invalid OTP. 2 attempt(s) remaining.",
  "action_time": "2025-01-05T10:35:00",
  "data": "Invalid OTP. 2 attempt(s) remaining."
}

Expired OTP (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "OTP has expired. Please request a new one.",
  "action_time": "2025-01-05T10:35:00",
  "data": "OTP has expired. Please request a new one."
}

Max Attempts Reached (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Maximum attempts reached. Please request a new OTP.",
  "action_time": "2025-01-05T10:35:00",
  "data": "Maximum attempts reached. Please request a new OTP."
}

3. Resend OTP

Purpose: Request a new OTP code using the existing token.

Endpoint: POST {base_url}/onboarding/auth-phone/resend-otp

Access Level: 🔒 Protected

Authentication: Bearer Token

Request JSON Sample:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Success Response Fields: Same as Request OTP endpoint

FieldDescription
totalUsersTotal registered users
completedOnboardingUsers who finished all onboarding steps
completionRatePercentage of users who completed onboarding
totalPagesConfiguredNumber of active preference pages
byStatusUser count per onboarding status
byAuthProviderUser count per authentication method
byLanguageUser count per language preference
byThemeUser count per theme preference

2.Email Verification Endpoints


4. Get DemographicsEmail Verification Status

Purpose: GetCheck detailedemail breakdownverification ofstatus userand responses per preference page.options.

Endpoint: GET {base_url}/onboarding/analytics/demographicsemail-verification/status

Access Level: 🔒 Protected (Admin Only)

Authentication: Bearer Token

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "DemographicsEmail verification status",
  "action_time": "2025-01-05T10:00:00",
  "data": {
    "verified": false,
    "email": "jo***@example.com",
    "required": false,
    "canSkip": true,
    "currentStep": "PENDING_EMAIL_VERIFICATION"
  }
}

5. Skip Email Verification

Purpose: Skip email verification step (if allowed by config).

Endpoint: POST {base_url}/onboarding/email-verification/skip

Access Level: 🔒 Protected

Authentication: Bearer Token

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Email verification skipped",
  "action_time": "2025-01-05T10:05:00",
  "data": {
    "verified": false,
    "skipped": true,
    "nextStep": "PENDING_PHONE_VERIFICATION"
  }
}

Preference Pages Endpoints


6. Get Onboarding Progress

Purpose: Get overall onboarding progress with all steps.

Endpoint: GET {base_url}/onboarding/progress

Access Level: 🔒 Protected

Authentication: Bearer Token

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Progress retrieved",
  "action_time": "2025-01-05T14:05:05T10:40:00",
  "data": {
    "totalUsersAnalyzed"percentage": 4200,45.0,
    "generatedAt"currentStage": "PENDING_PREFERENCES",
    "currentStageLabel": "Complete your preferences",
    "steps": [
      {
        "key": "registration",
        "label": "Registration",
        "completed": true,
        "weight": 15.0,
        "skippable": false
      },
      {
        "key": "phone_verification",
        "label": "Phone Verification",
        "completed": true,
        "weight": 15.0,
        "skippable": false
      },
      {
        "key": "page_interests",
        "label": "Your Interests",
        "completed": true,
        "weight": 13.33,
        "skippable": false
      },
      {
        "key": "page_goals",
        "label": "Your Goals",
        "completed": false,
        "weight": 13.33,
        "skippable": true
      },
      {
        "key": "page_experience",
        "label": "Your Experience",
        "completed": false,
        "weight": 13.33,
        "skippable": false
      },
      {
        "key": "profile_completion",
        "label": "Complete Profile",
        "completed": false,
        "weight": 15.0,
        "skippable": false
      }
    ],
    "nextStep": {
      "key": "page_goals",
      "label": "Your Goals",
      "endpoint": "/api/v1/onboarding/pages?page=2",
      "skippable": true
    }
  }
}

7. Get All Pages

Purpose: Get all preference pages with completion status.

Endpoint: GET {base_url}/onboarding/pages

Access Level: 🔒 Protected

Authentication: Bearer Token

Query Parameters:

ParameterTypeRequiredDescription
allbooleanNoReturn all pages (default behavior)
pageintegerNoGet specific page by order (1, 2, 3...)
categorystringNoGet page by category key
currentbooleanNoGet first incomplete page

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "All pages retrieved",
  "action_time": "2025-01-05T14:05:05T10:45:00",
  "data": {
    "totalPages": 3,
    "completedPages": 1,
    "isOnboardingComplete": false,
    "pages": [
      {
        "pageId"id": "550e8400-e29b-41d4-a716-446655440001",
        "pageOrder": 1,
        "categoryKey": "interests",
        "title": "YourMaslahi Interests"Yako",
        "pageOrder"description": "Chagua mambo yanayokuvutia",
        "bannerImages": ["https://cdn.fursahub.com/onboarding/interests.jpg"],
        "isSkippable": false,
        "minSelections": 1,
        "totalResponses"maxSelections": 4200,
        "skippedCount": 0,
        "responseRate": 100.0,5,
        "options": [
          { "key": "jobs", "label": "Jobs"Kazi", "count"icon": 2520, "percentage": 60.0briefcase" },
          { "key": "funding", "label": "Funding"Ufadhili", "count"icon": 1890, "percentage": 45.0dollar" },
          { "key": "events", "label": "Events"Matukio", "count"icon": 1260, "percentage": 30.0calendar" },
          { "key": "skills", "label": "Skills Training"Ujuzi", "count"icon": 1680, "percentage": 40.0book" },
          { "key": "networking", "label": "Networking"Mitandao", "count"icon": 840, "percentage": 20.0users" }
        ],
        "isCompleted": true
      },
      {
        "pageId"id": "550e8400-e29b-41d4-a716-446655440002",
        "pageOrder": 2,
        "categoryKey": "goals",
        "title": "YourMalengo Goals"Yako",
        "pageOrder"description": 2,"Unataka kufikia nini?",
        "totalResponses"isSkippable": 4100,true,
        "skippedCount"minSelections": 100,1,
        "responseRate"maxSelections": 97.6,3,
        "options": [
          { "key": "find_job", "label": "FindKupata a Job"kazi", "count"icon": 2460, "percentage": 60.0search" },
          { "key": "start_business", "label": "StartKuanzisha Business"biashara", "count"icon": 1845, "percentage": 45.0store" },
          { "key": "learn_skills", "label": "LearnKujifunza Skills"ujuzi", "count"icon": 1640, "percentage": 40.0graduation" },
          { "key": "get_funding", "label": "GetKupata Funding"ufadhili", "count"icon": 1230, "percentage": 30.0money" }
        ]
      },
      {
        "pageId": "550e8400-e29b-41d4-a716-446655440003",
        "categoryKey"isCompleted": "experience",
        "title": "Your Experience",
        "pageOrder": 3,
        "totalResponses": 4050,
        "skippedCount": 150,
        "responseRate": 96.4,
        "options": [
          { "key": "student", "label": "Student", "count": 1215, "percentage": 30.0 },
          { "key": "0-2_years", "label": "0-2 Years", "count": 1012, "percentage": 25.0 },
          { "key": "3-5_years", "label": "3-5 Years", "count": 810, "percentage": 20.0 },
          { "key": "5+_years", "label": "5+ Years", "count": 607, "percentage": 15.0 },
          { "key": "business_owner", "label": "Business Owner", "count": 405, "percentage": 10.0 }
        ]false
      }
    ]
  }
}

Success Response Fields:

FieldDescription
totalUsersAnalyzedUsers who responded to at least one page
generatedAtTimestamp when data was generated
pages[].totalResponsesUsers who responded to this page
pages[].skippedCountUsers who skipped this page
pages[].responseRatePercentage of users who responded
pages[].options[].countNumber of users who selected this option
pages[].options[].percentagePercentage of respondents who selected this option

3.8. Get Current Page Analytics

Purpose: Get detailedthe analyticsfirst for a specificincomplete preference page.

Endpoint: GET {base_url}/onboarding/analytics/page/pages?current=true

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Current page retrieved",
  "action_time": "2025-01-05T10:50:00",
  "data": {
    "page": {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "pageOrder": 2,
      "categoryKey": "goals",
      "title": "Malengo Yako",
      "description": "Unataka kufikia nini?",
      "isSkippable": true,
      "minSelections": 1,
      "maxSelections": 3,
      "options": [...]
    },
    "progress": {
      "current": 2,
      "total": 3,
      "nextPage": 3,
      "isLast": false,
      "isCompleted": false
    }
  }
}

9. Submit Page Response

Purpose: Save user's selections for a preference page.

Endpoint: POST {base_url}/onboarding/pages/{pageId}/response

Access Level: 🔒 Protected

Authentication: Bearer Token

Path Parameters:

ParameterTypeRequiredDescription
pageIdUUIDYesPage identifier

Request JSON Sample:

{
  "selectedOptions": ["find_job", "learn_skills"]
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
selectedOptionsarrayYesArray of selected option keysMust match page's min/max selections

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Response saved",
  "action_time": "2025-01-05T10:55:00",
  "data": {
    "saved": true,
    "progress": {
      "current": 2,
      "total": 3,
      "nextPage": 3,
      "isLast": false,
      "isCompleted": false
    }
  }
}

Error Responses:

Too Few Selections (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Minimum 1 selection(s) required",
  "action_time": "2025-01-05T10:55:00",
  "data": "Minimum 1 selection(s) required"
}

Invalid Option (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid option: unknown_key",
  "action_time": "2025-01-05T10:55:00",
  "data": "Invalid option: unknown_key"
}

10. Skip Page

Purpose: Skip a preference page (only if page is skippable).

Endpoint: POST {base_url}/onboarding/pages/{pageId}/skip

Access Level: 🔒 Protected

Authentication: Bearer Token

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Page skipped",
  "action_time": "2025-01-05T11:00:00",
  "data": {
    "saved": true,
    "progress": {
      "current": 2,
      "total": 3,
      "nextPage": 3,
      "isLast": false,
      "isCompleted": false
    }
  }
}

Error Response (Page Not Skippable):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "This page cannot be skipped",
  "action_time": "2025-01-05T11:00:00",
  "data": "This page cannot be skipped"
}

Language Preference Endpoint


11. Set Language Preference

Purpose: Update user's language preference (can be called anytime during onboarding).

Endpoint: POST {base_url}/onboarding/language-preference

Access Level: 🔒 Protected

Authentication: Bearer Token

Request JSON Sample:

{
  "code": "sw"
}

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Language preference updated",
  "action_time": "2025-01-05T11:05:00",
  "data": {
    "code": "sw",
    "name": "Swahili",
    "nativeName": "Kiswahili"
  }
}

Admin: Manage Onboarding Pages

These endpoints allow admins to create, edit, reorder, and manage onboarding preference pages without code changes.


12. Get All Pages (Admin)

Purpose: Get all onboarding pages with full details for admin management.

Endpoint: GET {base_url}/onboarding/pages/manage

Access Level: 🔒 Protected (Admin/Moderator)

Authentication: Bearer Token (ROLE_ADMIN, ROLE_SUPER_ADMIN, ROLE_MODERATOR)

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Pages retrieved",
  "action_time": "2025-01-05T12:00:00",
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "categoryKey": "interests",
      "pageOrder": 1,
      "isActive": true,
      "isSkippable": false,
      "minSelections": 1,
      "maxSelections": 5,
      "bannerImages": ["https://cdn.fursahub.com/onboarding/interests.jpg"],
      "translations": {
        "en": {
          "title": "Your Interests",
          "description": "Select what interests you"
        },
        "sw": {
          "title": "Maslahi Yako",
          "description": "Chagua mambo yanayokuvutia"
        }
      },
      "options": [
        {
          "key": "jobs",
          "icon": "briefcase",
          "translations": {
            "en": "Jobs",
            "sw": "Kazi"
          }
        },
        {
          "key": "funding",
          "icon": "dollar",
          "translations": {
            "en": "Funding",
            "sw": "Ufadhili"
          }
        }
      ],
      "createdAt": "2025-01-01T00:00:00",
      "updatedAt": "2025-01-05T10:00:00"
    }
  ]
}

13. Get Page by ID (Admin)

Purpose: Get single page details for editing.

Endpoint: GET {base_url}/onboarding/pages/manage/{pageId}

Access Level: 🔒 Protected (AdminAdmin/Moderator)

Only)

Path Parameters:

ParameterTypeRequiredDescription
pageIdUUIDYesPage identifier

Success Response: Same structure as single item in Get All Pages


14. Create Page

Purpose: Create a new onboarding preference page.

Endpoint: POST {base_url}/onboarding/pages/manage

Access Level: 🔒 Protected (Admin/Moderator)

Authentication: Bearer Token

Request JSON Sample:

{
  "categoryKey": "location",
  "pageOrder": 4,
  "isActive": true,
  "isSkippable": true,
  "minSelections": 1,
  "maxSelections": 1,
  "bannerImages": ["https://cdn.fursahub.com/onboarding/location.jpg"],
  "translations": {
    "en": {
      "title": "Your Location",
      "description": "Where are you based?"
    },
    "sw": {
      "title": "Mahali Ulipo",
      "description": "Unaishi wapi?"
    }
  },
  "options": [
    {
      "key": "dar_es_salaam",
      "icon": "map-pin",
      "translations": {
        "en": "Dar es Salaam",
        "sw": "Dar es Salaam"
      }
    },
    {
      "key": "arusha",
      "icon": "map-pin",
      "translations": {
        "en": "Arusha",
        "sw": "Arusha"
      }
    },
    {
      "key": "mwanza",
      "icon": "map-pin",
      "translations": {
        "en": "Mwanza",
        "sw": "Mwanza"
      }
    },
    {
      "key": "other",
      "icon": "map",
      "translations": {
        "en": "Other",
        "sw": "Nyingine"
      }
    }
  ]
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
categoryKeystringYesUnique category identifierLowercase, no spaces
pageOrderintegerYesDisplay order (1, 2, 3...)Min: 1
isActivebooleanNoPage is activeDefault: true
isSkippablebooleanNoUser can skip this pageDefault: false
minSelectionsintegerNoMinimum options to selectDefault: 1
maxSelectionsintegerNoMaximum options to selectDefault: 10
bannerImagesarrayNoBanner image URLsArray of URLs
translationsobjectYesTitle/description per languageMust include "en"
translations.{lang}.titlestringYesPage titleMax 100 chars
translations.{lang}.descriptionstringNoPage descriptionMax 500 chars
optionsarrayYesSelectable optionsMin 2 options
options[].keystringYesUnique option keyLowercase, no spaces
options[].iconstringNoIcon nameFrom icon library
options[].translationsobjectYesLabel per languageMust include "en"

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "CREATED",
  "message": "Page created",
  "action_time": "2025-01-05T12:05:00",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440004",
    "categoryKey": "location",
    "pageOrder": 4,
    "isActive": true,
    ...
  }
}

Error Responses:

Duplicate Category Key (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Category key already exists: interests",
  "action_time": "2025-01-05T12:05:00",
  "data": "Category key already exists: interests"
}

15. Update Page

Purpose: Update an existing onboarding page.

Endpoint: PUT {base_url}/onboarding/pages/manage/{pageId}

Access Level: 🔒 Protected (Admin/Moderator)

Path Parameters:

ParameterTypeRequiredDescription
pageIdUUIDYesPage identifier

Request JSON Sample: Same as Create Page

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Page updated",
  "action_time": "2025-01-05T12:10:00",
  "data": { ... }
}

16. Delete Page

Purpose: Delete an onboarding page (soft delete recommended - use deactivate instead).

Endpoint: DELETE {base_url}/onboarding/pages/manage/{pageId}

Access Level: 🔒 Protected (Admin/Moderator)

Path Parameters:

Parameter Type Required Description
pageId UUID Yes Page identifier

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Page analytics retrieved"deleted",
  "action_time": "2025-01-05T14:10:05T12:15:00",
  "data": {
    "pageId": "550e8400-e29b-41d4-a716-446655440001",
    "categoryKey": "interests",
    "title": "Your Interests",
    "pageOrder": 1,
    "totalResponses": 4200,
    "skippedCount": 0,
    "responseRate": 100.0,
    "options": [
      { "key": "jobs", "label": "Jobs", "count": 2520, "percentage": 60.0 },
      { "key": "funding", "label": "Funding", "count": 1890, "percentage": 45.0 }
    ]
  }null
}

4.17. GetActivate Page Analytics by Category

Purpose: Get analytics forActivate a pagedeactivated using category key instead of UUID.page.

Endpoint: GETPATCH {base_url}/onboarding/analytics/category/pages/manage/{categoryKey}pageId}/activate

Access Level: 🔒 Protected (Admin Only)Admin/Moderator)

PathSuccess Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Page activated",
  "action_time": "2025-01-05T12:20:00",
  "data": null
}

18. Deactivate Page

Purpose: Deactivate a page (hides from users without deleting).

Endpoint: PATCH {base_url}/onboarding/pages/manage/{pageId}/deactivate

Access Level: 🔒 Protected (Admin/Moderator)

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Page deactivated",
  "action_time": "2025-01-05T12:25:00",
  "data": null
}

19. Reorder Pages

Purpose: Change the display order of all pages at once.

Endpoint: PATCH {base_url}/onboarding/pages/manage/reorder

Access Level: 🔒 Protected (Admin/Moderator)

Request JSON Sample:

{
  "pageIds": [
    "550e8400-e29b-41d4-a716-446655440002",
    "550e8400-e29b-41d4-a716-446655440001",
    "550e8400-e29b-41d4-a716-446655440003",
    "550e8400-e29b-41d4-a716-446655440004"
  ]
}

Request Body Parameters:

Parameter Type Required Description
categoryKeystringYesPage category key (e.g., "interests", "goals")

Success Response: Same as Get Page Analytics


5. Get User Demographics

Purpose: Get all preference selections for a specific user.

Endpoint: GET {base_url}/onboarding/analytics/user/{userId}

Access Level: 🔒 Protected (Admin Only)

Path Parameters:

ParameterTypeRequiredDescriptionValidation
userIdpageIds UUIDarray Yes UserPage identifierIDs in new orderMust include all active page IDs

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "UserPages demographics retrieved"reordered",
  "action_time": "2025-01-05T14:15:05T12:30:00",
  "data": {
    "userId": "550e8400-e29b-41d4-a716-446655440000",
    "preferences": {
      "interests": ["jobs", "funding", "skills"],
      "goals": ["find_job", "learn_skills"],
      "experience": ["3-5_years"]
    }
  }null
}

6. Get Users by Selection

Purpose: Find all users who selected a specific option in a category.

Endpoint: GET {base_url}/onboarding/analytics/users/by-selection

Access Level: 🔒 Protected (Admin Only)

Panel

QueryImplementation Parameters:

ParameterTypeRequiredDescription
categoryKeystringYesPage category (e.g., "interests")
optionKeystringYesOption key (e.g., "jobs")

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Users retrieved",
  "action_time": "2025-01-05T14:20:00",
  "data": {
    "categoryKey": "interests",
    "optionKey": "jobs",
    "count": 2520,
    "userIds": [
      "550e8400-e29b-41d4-a716-446655440000",
      "550e8400-e29b-41d4-a716-446655440001",
      "550e8400-e29b-41d4-a716-446655440002"
    ]
  }
}

Purpose: Get top selected options for a category.

Endpoint: GET {base_url}/onboarding/analytics/popular-options/{categoryKey}

Access Level: 🔒 Protected (Admin Only)

Path Parameters:

ParameterTypeRequiredDescription
categoryKeystringYesPage category

Query Parameters:

ParameterTypeRequiredDescriptionDefault
limitintegerNoMax results to return10

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Popular options retrieved",
  "action_time": "2025-01-05T14:25:00",
  "data": {
    "categoryKey": "interests",
    "options": ["jobs", "funding", "skills", "events", "networking"]
  }
}

8. Get Similar Users

Purpose: Find users with similar preferences (for recommendation testing).

Endpoint: GET {base_url}/onboarding/analytics/user/{userId}/similar

Access Level: 🔒 Protected (Admin Only)

Path Parameters:

ParameterTypeRequiredDescription
userIdUUIDYesReference user

Query Parameters:

ParameterTypeRequiredDescriptionDefault
limitintegerNoMax similar users to return10

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Similar users retrieved",
  "action_time": "2025-01-05T14:30:00",
  "data": {
    "userId": "550e8400-e29b-41d4-a716-446655440000",
    "similarUsers": [
      "550e8400-e29b-41d4-a716-446655440010",
      "550e8400-e29b-41d4-a716-446655440011",
      "550e8400-e29b-41d4-a716-446655440012"
    ],
    "count": 3
  }
}

Internal Service Methods

These methods are available in OnboardingAnalyticsService for use by other backend services (recommendation engine, social media features, etc.):

// Get user's selections for a specific category
List<String> getUserSelections(UUID userId, String categoryKey);
// Example: getUserSelections(userId, "interests") → ["jobs", "funding"]

// Get complete user demographics
UserDemographicDto getUserDemographics(UUID userId);
// Returns all preferences across all categories

// Find users who selected specific option
List<UUID> getUsersBySelection(String categoryKey, String optionKey);
// Example: getUsersBySelection("goals", "start_business") → [userId1, userId2, ...]

// Find users who selected any of multiple options
List<UUID> getUsersBySelections(String categoryKey, List<String> optionKeys);

// Check if user selected a specific option
boolean hasUserSelected(UUID userId, String categoryKey, String optionKey);
// Example: hasUserSelected(userId, "interests", "jobs") → true

// Find users with similar preferences (for recommendations)
List<UUID> getUsersWithSimilarPreferences(UUID userId, int limit);

// Get most popular options in a category
List<String> getMostPopularOptions(String categoryKey, int limit);

Admin Dashboard UsageGuide

SummaryPage CardsList View

GET /onboarding/analytics/summarypages/manage
├── CardDisplay 1:table Totalwith: UsersOrder, Category, Title (totalUsers)en), Status, Actions
├── CardActions: 2:Edit, CompletionActivate/Deactivate, Rate (completionRate%)Delete
├── CardDrag-and-drop 3:for Activereorder Today (calculatePATCH from other data)/onboarding/pages/manage/reorder
└── Card"Add 4:New PendingPage" Verification (byStatus.PENDING_*)button

PieCreate/Edit ChartsPage Form

Fields:
├── Category Key (text, required, unique)
├── Page Order (number)
├── Is Skippable (checkbox)
├── Min/Max Selections (numbers)
├── Banner Images (file upload → Files API)
├── Translations (tabs for each language)
│   ├── Title (text)
│   └── Description (textarea)
└── Options (repeater)
    ├── Key (text, unique within page)
    ├── Icon (icon picker)
    └── Translations (text per language)

On Save:
├── New: POST /onboarding/pages/manage
└── Edit: PUT /onboarding/pages/manage/{pageId}

Quick Actions

Activate: PATCH /onboarding/pages/manage/{pageId}/activate
Deactivate: PATCH /onboarding/pages/manage/{pageId}/deactivate
Delete: DELETE /onboarding/pages/manage/{pageId} (with confirmation)

Frontend Implementation Guide

Phone Verification Screen

1. Show phone input with country code selector
2. On submit: POST /onboarding/auth-phone/request-otp
3. Save token from response
4. Navigate to OTP input screen
5. Show countdown for resendAvailableIn
6. On OTP submit: POST /onboarding/auth-phone/verify
7. On success: Navigate based on nextStep

Preference Pages Loop

1. GET /onboarding/pages?current=true
2. If page is null → All done, navigate to profile
3. Display page with:
   ├── Title, description (translated)
   ├── Banner image
   ├── Options as selectable chips/cards
   └── Skip button (if isSkippable)
4. On submit: POST /onboarding/pages/{pageId}/response
5. Check progress.isCompleted
   ├── true → Navigate to profile completion
   └── false → GET /onboarding/pages?current=true (loop)

Progress Indicator

GET /onboarding/analytics/summaryprogress
├── ChartUse 1:percentage Usersfor byprogress Auth Provider (byAuthProvider)
├── Chart 2: Users by Language (byLanguage)
└── Chart 3: Users by Theme (byTheme)

Demographics Bar Charts

GET /onboarding/analytics/demographics
For each page:
├── Chart: Option distributionbar
├── Show percentagesteps barsas dots/icons
├── Highlight topcurrent selections

User Lookup

GET /onboarding/analytics/user/{userId}
├── Show all user preferences
├── Link to full profilestep
└── Show similarcompleted userssteps with checkmarks