Skip to main content

NextGate Authentication

Author: Josh S. Sakweli,NextGate Backend Lead Team
Last Updated: 2025-09-2301-24
Version: v1.0

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

Short Description: TheNextGate Authentication Service handles user registration, login, OTP verification, password reset, and token management for the NextGate social commerce platform. This serviceAPI provides securea accountcomprehensive, managementpasswordless-first authentication system with OAuth2 support (Google, Apple), multi-channel OTP verificationverification, device tracking, session management, and JWT-baseda authentication.6-step user onboarding flow. Designed for mobile-first applications with security features including risk assessment, rate limiting, and refresh token rotation.

Hints:

  • ๐Ÿ” Passwordless by Default: Users can authenticate via OTP (SMS/Email) without ever setting a password
  • ๐Ÿ“ฑ Device Trust: Each device is tracked and can be managed by the user
  • ๐Ÿ”„ Token Rotation: Refresh tokens are rotated on each use for enhanced security
  • โฑ๏ธ OTP Validity: All OTP codes areexpire 6-digitin numbers10 minutes with 10-minutemax expiration
  • 3
  • Rate limiting: Maximum 5 OTP requests per 10-minute window
  • Resend cooldown: 2 minutes between resendverification attempts
  • JWT๐ŸŒ tokensMulti-Channel: OTP can be sent via Email or SMS based on user preference
  • ๐Ÿ“Š Metadata System: Onboarding responses include accessnextStepMetadata tokensfor (short-lived)pre-filling and refresh tokens (long-lived)
  • Phone numbers must be in international E.164 format (+1234567890)
  • Passwords must meet strong security requirements (8+ chars, uppercase, lowercase, digit, special character)forms

Authentication Architecture Overview

EndpointsSystem Design Philosophy

NextGate Authentication is built on three core principles:

  1. Passwordless-First: Users authenticate primarily via OTP, with password as an optional secondary method
  2. Device-Aware Security: Every login tracks device information for risk assessment
  3. Progressive Onboarding: New users complete a 6-step guided setup process

1.Authentication Flow Diagram

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        NEXTGATE AUTHENTICATION FLOW                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                              โ”‚   START      โ”‚
                              โ”‚  /auth/start โ”‚
                              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                     โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚                โ”‚                โ”‚
                    โ–ผ                โ–ผ                โ–ผ
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ”‚   PHONE   โ”‚    โ”‚   EMAIL   โ”‚    โ”‚  USERNAME โ”‚
            โ”‚ +255...   โ”‚    โ”‚ @email.comโ”‚    โ”‚  @handle  โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚                โ”‚                โ”‚
                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                   โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚                             โ”‚
                    โ–ผ                             โ–ผ
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ”‚   NEW USER    โ”‚            โ”‚ EXISTING USER โ”‚
            โ”‚               โ”‚            โ”‚               โ”‚
            โ”‚ โ€ข Send OTP    โ”‚            โ”‚ Has Password? โ”‚
            โ”‚ โ€ข Create      โ”‚            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
            โ”‚   Account     Registrationโ”‚                    โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚                  โ”‚                 โ”‚
                    โ”‚                  โ–ผ                 โ–ผ
                    โ”‚          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚          โ”‚  PASSWORD   โ”‚   โ”‚ PASSWORDLESSโ”‚
                    โ”‚          โ”‚   LOGIN     โ”‚   โ”‚  (OTP SENT) โ”‚
                    โ”‚          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚                 โ”‚                 โ”‚
                    โ”‚                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚                          โ”‚
                    โ–ผ                          โ–ผ
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ”‚  VERIFY OTP   โ”‚          โ”‚ DEVICE CHECK  โ”‚
            โ”‚ /auth/verify  โ”‚          โ”‚               โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚ Known Device? โ”‚
                    โ”‚                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚                          โ”‚
                    โ”‚                โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚                โ”‚                   โ”‚
                    โ”‚                โ–ผ                   โ–ผ
                    โ”‚        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚        โ”‚   KNOWN     โ”‚     โ”‚    NEW      โ”‚
                    โ”‚        โ”‚   DEVICE    โ”‚     โ”‚   DEVICE    โ”‚
                    โ”‚        โ”‚             โ”‚     โ”‚             โ”‚
                    โ”‚        โ”‚ โ†’ Login OK  โ”‚     โ”‚ โ†’ Verify    โ”‚
                    โ”‚        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚    via OTP  โ”‚
                    โ”‚                            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚                                   โ”‚
                    โ–ผ                                   โ–ผ
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ”‚                  OTP VERIFIED                      โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚                               โ”‚
                    โ–ผ                               โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚   ONBOARDING      โ”‚           โ”‚   FULLY           โ”‚
        โ”‚   REQUIRED        โ”‚           โ”‚   AUTHENTICATED   โ”‚
        โ”‚                   โ”‚           โ”‚                   โ”‚
        โ”‚ (New User or      โ”‚           โ”‚ โ†’ Access Token    โ”‚
        โ”‚  Incomplete)      โ”‚           โ”‚ โ†’ Refresh Token   โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚              6-STEP ONBOARDING              โ”‚
        โ”‚                                             โ”‚
        โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”     โ”‚
        โ”‚  โ”‚ 1.  โ”‚โ”€โ”€โ–ถโ”‚ 2.  โ”‚โ”€โ”€โ–ถโ”‚ 3.  โ”‚โ”€โ”€โ–ถโ”‚ 4.  โ”‚     โ”‚
        โ”‚  โ”‚ OTP โ”‚   โ”‚ AGE โ”‚   โ”‚USER โ”‚   โ”‚INT- โ”‚     โ”‚
        โ”‚  โ”‚VRFY โ”‚   โ”‚+NAMEโ”‚   โ”‚NAME โ”‚   โ”‚ESTS โ”‚     โ”‚
        โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
        โ”‚                                  โ”‚          โ”‚
        โ”‚                                  โ–ผ          โ”‚
        โ”‚                             โ”Œโ”€โ”€โ”€โ”€โ”€โ”        โ”‚
        โ”‚                             โ”‚ 5.  โ”‚        โ”‚
        โ”‚                             โ”‚PRFL โ”‚        โ”‚
        โ”‚                             โ””โ”€โ”€โ”ฌโ”€โ”€โ”˜        โ”‚
        โ”‚                                โ”‚           โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                         โ”‚
                                         โ–ผ
                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                              โ”‚     COMPLETE      โ”‚
                              โ”‚                   โ”‚
                              โ”‚ โ†’ Access Token    โ”‚
                              โ”‚ โ†’ Refresh Token   โ”‚
                              โ”‚ โ†’ Welcome!        โ”‚
                              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Token Types

TokenPurposeExpiryStorage
tempTokenOTP verification, flow state10 minMemory only
onboardingTokenOnboarding step progression30 minMemory only
onboardingRefreshTokenRefresh onboarding token24 hoursSecure storage
accessTokenAPI authentication1 hourSecure storage
refreshTokenGet new access tokens30 daysSecure storage
deviceVerificationTokenNew device verification10 minMemory only

Onboarding Steps Explained

StepEnum ValueWhat HappensMetadata Provided
1INITIATEDAccount created, OTP sent-
2OTP_VERIFIEDOTP confirmed, ready for agefirstName, lastName, profilePicture (from OAuth)
3AGE_VERIFIEDAge confirmed, names savedsuggestedUsernames (5 smart suggestions)
4USERNAME_SETUsername chosen-
5INTERESTS_SELECTEDInterests savedfirstName, lastName, username, suggestedDisplayName, profilePicture
6COMPLETEDProfile complete, tokens issued-

Device Fingerprinting Guide

Why Device Fingerprinting?

Device fingerprinting creates a unique identifier for each device/browser combination. This enables:

  • PurposeSecurity: RegisterDetect new/unknown devices attempting login
  • Trust Levels: Known devices can skip additional verification
  • Session Management: Users can see and revoke device access

Implementation for Frontend

npm install @fingerprintjs/fingerprintjs

React Implementation

// hooks/useDeviceFingerprint.js
import { useState, useEffect } from 'react';
import FingerprintJS from '@fingerprintjs/fingerprintjs';

export const useDeviceFingerprint = () => {
  const [fingerprint, setFingerprint] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const generateFingerprint = async () => {
      try {
        const fp = await FingerprintJS.load();
        const result = await fp.get();
        
        setFingerprint({
          deviceId: result.visitorId,
          confidence: result.confidence.score,
          components: {
            platform: result.components.platform?.value,
            timezone: result.components.timezone?.value,
            language: result.components.languages?.value?.[0],
            screenResolution: result.components.screenResolution?.value,
            colorDepth: result.components.colorDepth?.value,
          }
        });
      } catch (error) {
        console.error('Fingerprint generation failed:', error);
        // Fallback: Generate a random UUID and store in localStorage
        let fallbackId = localStorage.getItem('ng_device_id');
        if (!fallbackId) {
          fallbackId = crypto.randomUUID();
          localStorage.setItem('ng_device_id', fallbackId);
        }
        setFingerprint({ deviceId: fallbackId, confidence: 0.5 });
      } finally {
        setLoading(false);
      }
    };

    generateFingerprint();
  }, []);

  return { fingerprint, loading };
};

Usage in Authentication

// components/LoginForm.jsx
import { useDeviceFingerprint } from '../hooks/useDeviceFingerprint';

const LoginForm = () => {
  const { fingerprint, loading } = useDeviceFingerprint();

  const handleStartAuth = async (identifier) => {
    if (loading || !fingerprint) {
      console.warn('Device fingerprint not ready');
      return;
    }

    const response = await fetch('/api/v1/auth/start', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        identifier: identifier,
        deviceId: fingerprint.deviceId,
        deviceName: getDeviceName(),
        platform: getPlatform(),
      }),
    });

    const data = await response.json();
    // Handle response...
  };

  return (/* Your form JSX */);
};

// Helper: Generate human-readable device name
const getDeviceName = () => {
  const ua = navigator.userAgent;
  let browser = 'Unknown Browser';
  let os = 'Unknown OS';

  if (ua.includes('Chrome')) browser = 'Chrome';
  else if (ua.includes('Firefox')) browser = 'Firefox';
  else if (ua.includes('Safari')) browser = 'Safari';
  else if (ua.includes('Edge')) browser = 'Edge';

  if (ua.includes('Windows')) os = 'Windows';
  else if (ua.includes('Mac')) os = 'macOS';
  else if (ua.includes('Linux')) os = 'Linux';
  else if (ua.includes('Android')) os = 'Android';
  else if (ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS';

  return `${browser} on ${os}`;
};

// Helper: Get platform type
const getPlatform = () => {
  const ua = navigator.userAgent;
  if (ua.includes('Android')) return 'ANDROID';
  if (ua.includes('iPhone') || ua.includes('iPad')) return 'IOS';
  return 'WEB';
};

Alternative: Custom Fingerprint (No Library)

// utils/customFingerprint.js
export const generateCustomFingerprint = async () => {
  const components = [];

  // Screen properties
  components.push(window.screen.width);
  components.push(window.screen.height);
  components.push(window.screen.colorDepth);
  components.push(window.devicePixelRatio);

  // Timezone & Language
  components.push(Intl.DateTimeFormat().resolvedOptions().timeZone);
  components.push(navigator.language);
  components.push(navigator.platform);
  components.push(navigator.hardwareConcurrency || 'unknown');

  // Canvas fingerprint
  try {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    ctx.textBaseline = 'top';
    ctx.font = '14px Arial';
    ctx.fillText('NextGate Device FP', 2, 2);
    components.push(canvas.toDataURL());
  } catch (e) {
    components.push('canvas-blocked');
  }

  // WebGL renderer
  try {
    const canvas = document.createElement('canvas');
    const gl = canvas.getContext('webgl');
    const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
    if (debugInfo) {
      components.push(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL));
    }
  } catch (e) {
    components.push('webgl-blocked');
  }

  // Create hash
  const fingerprint = components.join('|||');
  const encoder = new TextEncoder();
  const data = encoder.encode(fingerprint);
  const hashBuffer = await crypto.subtle.digest('SHA-256', data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
};

Required Headers for All Auth Requests

const authHeaders = {
  'Content-Type': 'application/json',
  'X-Device-Id': fingerprint.deviceId,
  'X-Device-Name': getDeviceName(),
  'X-Platform': getPlatform(),
};

// For authenticated requests:
authHeaders['Authorization'] = `Bearer ${accessToken}`;

// For session management:
authHeaders['X-Session-Id'] = sessionId;

Onboarding Flow

Visual Flow with API Calls

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         COMPLETE ONBOARDING FLOW                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

STEP 0: ENTRY POINT
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚                    Two Entry Points                         โ”‚
     โ”‚                                                             โ”‚
     โ”‚   A) Phone/Email Login          B) OAuth (Google/Apple)    โ”‚
     โ”‚      POST /auth/start              POST /auth/login/oauth  โ”‚
     โ”‚           โ”‚                              โ”‚                 โ”‚
     โ”‚           โ–ผ                              โ–ผ                 โ”‚
     โ”‚      OTP Sent to User            Google Validates User     โ”‚
     โ”‚           โ”‚                              โ”‚                 โ”‚
     โ”‚           โ–ผ                              โ”‚                 โ”‚
     โ”‚      POST /auth/verify                   โ”‚                 โ”‚
     โ”‚           โ”‚                              โ”‚                 โ”‚
     โ”‚           โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
     โ”‚                          โ”‚                                 โ”‚
     โ”‚                          โ–ผ                                 โ”‚
     โ”‚              Response contains:                            โ”‚
     โ”‚              โ€ข onboardingToken                             โ”‚
     โ”‚              โ€ข currentStep                                 โ”‚
     โ”‚              โ€ข nextStep: AGE_VERIFIED                      โ”‚
     โ”‚              โ€ข nextStepMetadata: {                         โ”‚
     โ”‚                  firstName: "John",    // from OAuth       โ”‚
     โ”‚                  lastName: "Doe",      // from OAuth       โ”‚
     โ”‚                  profilePicture: "..." // from OAuth       โ”‚
     โ”‚                  hasOAuthData: true                        โ”‚
     โ”‚                }                                           โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
STEP 1: AGE & NAME VERIFICATION (POST /auth/onboarding/age)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚  FRONTEND SHOWS:                                           โ”‚
     โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
     โ”‚  โ”‚  ๐Ÿ“… Let's verify your age                            โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  First Name: [John____________] โ† Pre-filled if OAuthโ”‚  โ”‚
     โ”‚  โ”‚  Last Name:  [Doe_____________] โ† Pre-filled if OAuthโ”‚  โ”‚
     โ”‚  โ”‚  Birth Date: [____/____/______]                      โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  [Continue โ†’]                                        โ”‚  โ”‚
     โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
     โ”‚                                                             โ”‚
     โ”‚  REQUEST:                                                   โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "firstName": "John",                                     โ”‚
     โ”‚    "lastName": "Doe",                                       โ”‚
     โ”‚    "birthDate": "1999-05-15"                                โ”‚
     โ”‚  }                                                          โ”‚
     โ”‚                                                             โ”‚
     โ”‚  RESPONSE:                                                  โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "accountTier": "FULL",                                   โ”‚
     โ”‚    "age": 25,                                               โ”‚
     โ”‚    "currentStep": "AGE_VERIFIED",                           โ”‚
     โ”‚    "nextStep": "USERNAME_SET",                              โ”‚
     โ”‚    "nextStepMetadata": {                                    โ”‚
     โ”‚      "suggestedUsernames": [                                โ”‚
     โ”‚        "johndoe99",                                         โ”‚
     โ”‚        "john_doe_official",                                 โ”‚
     โ”‚        "jdoe_tz",                                           โ”‚
     โ”‚        "the_johndoe",                                       โ”‚
     โ”‚        "johnd_pro"                                          โ”‚
     โ”‚      ]                                                      โ”‚
     โ”‚    }                                                        โ”‚
     โ”‚  }                                                          โ”‚
     โ”‚                                                             โ”‚
     โ”‚  โš ๏ธ If age < 13: User blocked, account deleted             โ”‚
     โ”‚  โš ๏ธ If age 13-17: Account tier = RESTRICTED                โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
STEP 2: USERNAME SELECTION (POST /auth/onboarding/username)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚  FRONTEND SHOWS:                                           โ”‚
     โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
     โ”‚  โ”‚  ๐Ÿ‘ค Choose your username                             โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  @[________________]                                 โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  Suggestions:                                        โ”‚  โ”‚
     โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚  โ”‚
     โ”‚  โ”‚  โ”‚ johndoe99   โ”‚ โ”‚ john_doe    โ”‚ โ”‚ jdoe_tz     โ”‚    โ”‚  โ”‚
     โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚  โ”‚
     โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                    โ”‚  โ”‚
     โ”‚  โ”‚  โ”‚ the_johndoe โ”‚ โ”‚ johnd_pro   โ”‚                    โ”‚  โ”‚
     โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                    โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  [Continue โ†’]                                        โ”‚  โ”‚
     โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
     โ”‚                                                             โ”‚
     โ”‚  REAL-TIME CHECK: POST /auth/onboarding/check-username     โ”‚
     โ”‚  โ†’ Returns availability + new suggestions if taken         โ”‚
     โ”‚                                                             โ”‚
     โ”‚  FINAL SUBMIT:                                              โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "username": "johndoe99"                                  โ”‚
     โ”‚  }                                                          โ”‚
     โ”‚                                                             โ”‚
     โ”‚  RESPONSE:                                                  โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "username": "johndoe99",                                 โ”‚
     โ”‚    "available": true,                                       โ”‚
     โ”‚    "currentStep": "USERNAME_SET",                           โ”‚
     โ”‚    "nextStep": "INTERESTS_SELECTED"                         โ”‚
     โ”‚  }                                                          โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
STEP 3: INTERESTS SELECTION (POST /auth/onboarding/interests)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚  FRONTEND SHOWS:                                           โ”‚
     โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
     โ”‚  โ”‚  ๐ŸŽฏ What are you interested in?                      โ”‚  โ”‚
     โ”‚  โ”‚  Select at least 3                                   โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚  โ”‚
     โ”‚  โ”‚  โ”‚๐ŸŽตMusic โ”‚ โ”‚โšฝSportsโ”‚ โ”‚๐ŸŽฎGamingโ”‚ โ”‚๐Ÿ“ฑTech  โ”‚        โ”‚  โ”‚
     โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚  โ”‚
     โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚  โ”‚
     โ”‚  โ”‚  โ”‚๐ŸŽฌMoviesโ”‚ โ”‚๐Ÿ“šBooks โ”‚ โ”‚๐Ÿ•Food  โ”‚ โ”‚โœˆ๏ธTravelโ”‚        โ”‚  โ”‚
     โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚  โ”‚
     โ”‚  โ”‚                                    ... more          โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  [Continue โ†’]                                        โ”‚  โ”‚
     โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
     โ”‚                                                             โ”‚
     โ”‚  FETCH CATEGORIES: GET /interests/categories/all           โ”‚
     โ”‚                                                             โ”‚
     โ”‚  SUBMIT:                                                    โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "interestIds": ["uuid-music", "uuid-tech", "uuid-travel"]โ”‚
     โ”‚  }                                                          โ”‚
     โ”‚                                                             โ”‚
     โ”‚  RESPONSE:                                                  โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "selectedInterests": ["Music", "Tech", "Travel"],        โ”‚
     โ”‚    "count": 3,                                              โ”‚
     โ”‚    "currentStep": "INTERESTS_SELECTED",                     โ”‚
     โ”‚    "nextStep": "PROFILE_COMPLETED",                         โ”‚
     โ”‚    "nextStepMetadata": {                                    โ”‚
     โ”‚      "firstName": "John",                                   โ”‚
     โ”‚      "lastName": "Doe",                                     โ”‚
     โ”‚      "username": "johndoe99",                               โ”‚
     โ”‚      "suggestedDisplayName": "John Doe",                    โ”‚
     โ”‚      "profilePicture": "https://..."                        โ”‚
     โ”‚    }                                                        โ”‚
     โ”‚  }                                                          โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
STEP 4: PROFILE COMPLETION (POST /auth/onboarding/profile)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚  FRONTEND SHOWS:                                           โ”‚
     โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
     โ”‚  โ”‚  โœจ Complete your profile                            โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚  โ”‚
     โ”‚  โ”‚       โ”‚  ๐Ÿ“ท     โ”‚  โ† Pre-filled from OAuth           โ”‚  โ”‚
     โ”‚  โ”‚       โ”‚ [image] โ”‚    or upload new                   โ”‚  โ”‚
     โ”‚  โ”‚       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  Display Name: [John Doe________] โ† Pre-filled       โ”‚  โ”‚
     โ”‚  โ”‚  @johndoe99 (cannot change here)                     โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  Bio: [_________________________________]            โ”‚  โ”‚
     โ”‚  โ”‚       [_________________________________]            โ”‚  โ”‚
     โ”‚  โ”‚                                                      โ”‚  โ”‚
     โ”‚  โ”‚  [๐ŸŽ‰ Complete Setup]                                 โ”‚  โ”‚
     โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
     โ”‚                                                             โ”‚
     โ”‚  REQUEST:                                                   โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "onboardingToken": "eyJ...",                             โ”‚
     โ”‚    "displayName": "John Doe",                               โ”‚
     โ”‚    "bio": "Tech enthusiast | Music lover",                  โ”‚
     โ”‚    "profilePictureUrl": "https://..."                       โ”‚
     โ”‚  }                                                          โ”‚
     โ”‚                                                             โ”‚
     โ”‚  HEADERS (for device registration):                         โ”‚
     โ”‚  X-Device-Id: "abc123fingerprint"                           โ”‚
     โ”‚  X-Device-Name: "Chrome on macOS"                           โ”‚
     โ”‚  X-Platform: "WEB"                                          โ”‚
     โ”‚                                                             โ”‚
     โ”‚  RESPONSE:                                                  โ”‚
     โ”‚  {                                                          โ”‚
     โ”‚    "accessToken": "eyJ...",                                 โ”‚
     โ”‚    "refreshToken": "eyJ...",                                โ”‚
     โ”‚    "systemUsername": "su_uuid",                             โ”‚
     โ”‚    "username": "johndoe99",                                 โ”‚
     โ”‚    "displayName": "John Doe",                               โ”‚
     โ”‚    "currentStep": "COMPLETED",                              โ”‚
     โ”‚    "onboardingComplete": true,                              โ”‚
     โ”‚    "message": "Welcome to NextGate!"                        โ”‚
     โ”‚  }                                                          โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   ๐ŸŽ‰ WELCOME!         โ”‚
                    โ”‚                       โ”‚
                    โ”‚   User is now fully   โ”‚
                    โ”‚   authenticated and   โ”‚
                    โ”‚   can access the app  โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Handling Onboarding Resume

When a user accountreturns andwith initiateincomplete OTPonboarding:

verification
const processhandleOnboardingResponse = (response) => {
  const { currentStep, nextStep, nextStepMetadata, onboardingToken } = response.data;

  sessionStorage.setItem('onboardingToken', onboardingToken);

  switch (nextStep) {
    case 'AGE_VERIFIED':
      navigate('/onboarding/age', { state: { metadata: nextStepMetadata } });
      break;
    case 'USERNAME_SET':
      navigate('/onboarding/username', { state: { metadata: nextStepMetadata } });
      break;
    case 'INTERESTS_SELECTED':
      navigate('/onboarding/interests');
      break;
    case 'PROFILE_COMPLETED':
      navigate('/onboarding/profile', { state: { metadata: nextStepMetadata } });
      break;
    case 'COMPLETED':
      handleFullAuth(response.data);
      break;
  }
};

Standard Response Format

All API responses follow a consistent structure:

EndpointSuccess Response Structure:

POST
{
  https://apinexgate.glueauth.com/api/v1/auth/register"success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-01-24T10:30:45",
  "data": { }
}

AccessError LevelResponse Structure:

๐ŸŒ
{
  Public"success": (Nofalse,
  authentication"httpStatus": required)

"BAD_REQUEST", "message": "Error description", "action_time": "2025-01-24T10:30:45", "data": "Error description" }

Authentication:Standard None

Response

Request HeadersFields:

be
HeaderField TypeRequired Description
Content-Typesuccessbooleantrue for successful operations, false for errors
httpStatus string YesHTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.)
message Muststring Human-readable "application/json"message
action_timestringISO 8601 timestamp
dataobject/stringResponse payload or error details

API Endpoints

Authentication Initialization

1. Start Authentication

Purpose: Initiate authentication flow for phone, email, or username

Endpoint: POST {base_url}/auth/start

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "phoneNumber"identifier": "+255712345678",
  "password"deviceId": "SecurePass123!"a1b2c3d4e5f6789",
  "email"deviceName": "john.doe@example.com"Chrome on macOS",
  "firstName"platform": "John",
  "lastName": "Doe",
  "middleName": "Michael",
  "verificationChannel": "EMAIL"WEB"
}

Request Body Parameters:

Parameter Type Required Description Validation
phoneNumberidentifier string Yes User'sPhone phone(+255...), numberemail, inor international format@username MustValid match E.164 format: +[country][number] (1-14 digits after +)format
passworddeviceId string Yes User'sDevice passwordfingerprint Min 8 chars, must contain uppercase, lowercase, digit, and special character (@$!%*?&#)Non-empty
emaildeviceName string Yes User'sHuman-readable emaildevice addressname Must be valid email formatNon-empty
firstNameplatform string Yes User'sDevice first nameMax 30 characters, cannot be blank
lastNamestringYesUser's last nameMax 30 characters, cannot be blank
middleNamestringYesUser's middle nameMax 30 characters, cannot be blank
verificationChannelstringYesOTP delivery methodplatform enum: EMAIL,IOS, SMS,ANDROID, WHATSAPP, VOICE_CALL, PUSH_NOTIFICATION, EMAIL_AND_SMS, SMS_AND_WHATSAPP, ALL_CHANNELSWEB

Success Response JSON Sample (New User):

{
  "success": true,
  "httpStatus": "OK",
  "message": "RegistrationAuthentication successful"initiated",
  "action_time": "2025-09-23T10:01-24T10:30:00"45",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "maskedIdentifier": "โ€ขโ€ขโ€ข โ€ขโ€ขโ€ข โ€ขโ€ข78",
    "provider": "PHONE",
    "otpExpiresIn": 600,
    "isNewUser": true,
    "onboardingComplete": false,
    "currentStep": "INITIATED",
    "hasPassword": false,
    "requiresOtpChannelSelection": false,
    "message": "OTP has been sent to yourโ€ขโ€ขโ€ข email",โ€ขโ€ขโ€ข "expireAt": "2025-09-23T10:40:00"โ€ขโ€ข78"
  }
}

Success Response JSON Sample (Existing User with Password):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Authentication initiated",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "maskedIdentifier": "jโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข@gโ€ขโ€ขโ€ขโ€ข.com",
    "provider": "EMAIL",
    "otpExpiresIn": 600,
    "isNewUser": false,
    "onboardingComplete": true,
    "currentStep": "COMPLETED",
    "hasPassword": true,
    "requiresOtpChannelSelection": false,
    "message": "Choose login method: password or OTP"
  }
}

Success Response JSON Sample (Username - Channel Selection Required):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Authentication initiated",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "hasPassword": true,
    "requiresOtpChannelSelection": true,
    "availableChannels": [
      { "type": "EMAIL", "maskedValue": "jโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข@gโ€ขโ€ขโ€ขโ€ข.com", "isPrimary": true },
      { "type": "PHONE", "maskedValue": "โ€ขโ€ขโ€ข โ€ขโ€ขโ€ข โ€ขโ€ข78", "isPrimary": false }
    ],
    "isNewUser": false,
    "onboardingComplete": true,
    "message": "Select where to receive OTP"
  }
}

Success Response Fields:

Field Description
successtempToken BooleanTemporary indicating operation success (always truetoken for successfulnext requests)step
httpStatusmaskedIdentifier HTTPMasked statusphone/email codefor as stringdisplay
messageprovider Human-readableIdentifier successtype: messagePHONE, EMAIL
action_timeotpExpiresIn ISO 8601 timestamp of when the response was generated
data.tempTokenJWT temporary token required for OTP verificationvalidity in seconds (expires600 in= 10 minutes)
data.messageisNewUser SpecificWhether messagethis aboutis a new registration
onboardingCompleteWhether user completed onboarding
currentStepCurrent onboarding step
hasPasswordWhether user has set a password
requiresOtpChannelSelectionIf true, user must choose OTP delivery channel
data.expireAtavailableChannels ISOList 8601of timestampavailable whenOTP the temp token expireschannels

Error Responses:

    Identifier

  • 400 Bad Request: Invalid request data, validation failures, or user already exists
  • 422 Unprocessable Entity: Business logic validation errorsBlocked (e.g., duplicate email/phone)
  • 500 Internal Server Error400): Server-side processing errors

Error Response Sample:

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "UserThis withphone providedis credentials already exist, please login"blocked",
  "action_time": "2025-09-23T10:01-24T10:30:00"45",
  "data": "UserThis withphone providedis credentialsblocked"
already}
exist,
please

Account login"Not Found - Username (404):

{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Account not found",
  "action_time": "2025-01-24T10:30:45",
  "data": "Account not found"
}

2. Verify RegistrationAuthentication OTP

Purpose: Complete user registration by verifying theVerify OTP codeand sentcomplete duringauthentication registrationor continue to onboarding

Endpoint: POST https:{base_url}//apinexgate.glueauth.com/api/v1/auth/verify-otpverify

Access Level: ๐ŸŒ Public (Requires temporary token from registration)

Authentication: None (uses tempToken from registration response)

Request Headers:

HeaderTypeRequiredDescription
Content-TypestringYesMust be "application/json"

Request JSON Sample:

{
  "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "otpCode"otp": "123456",
  "deviceId": "a1b2c3d4e5f6789",
  "deviceName": "Chrome on macOS",
  "platform": "WEB"
}

Request Body Parameters:

(\d{6})
Parameter Type Required Description Validation
tempToken string Yes Temporary JWT tokenToken from registration response/auth/start MustValid be valid non-expired temp tokenJWT
otpCodeotp string Yes 6-digit OTP code received via chosen channel Must be exactlyExactly 6 digits
deviceIdstringNoDevice fingerprint-
deviceNamestringNoHuman-readable device name-
platformstringNoDevice platformenum: IOS, ANDROID, WEB

Success Response JSON Sample (New User - Start Onboarding):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Verification successful",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "onboardingComplete": false,
    "currentStep": "OTP_VERIFIED",
    "nextStep": "AGE_VERIFIED",
    "nextStepMetadata": null,
    "message": "OTP verified. Let's set up your account."
  }
}

Success Response JSON Sample (OAuth User - Has Profile Data):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Verification successful",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "onboardingComplete": false,
    "currentStep": "OTP_VERIFIED",
    "nextStep": "AGE_VERIFIED",
    "nextStepMetadata": {
      "firstName": "John",
      "lastName": "Doe",
      "profilePicture": "https://lh3.googleusercontent.com/...",
      "hasOAuthData": true
    },
    "message": "OTP verified. Let's set up your account."
  }
}

Success Response JSON Sample (Existing User - Fully Authenticated):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Verification successful",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "systemUsername": "su_550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe99",
    "displayName": "John Doe",
    "accountTier": "FULL",
    "onboardingComplete": true,
    "currentStep": "COMPLETED",
    "message": "Login successful"
  }
}

Error Responses:

Invalid OTP (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Invalid OTP code",
  "action_time": "2025-01-24T10:30:45",
  "data": "Invalid OTP code"
}

Max Attempts Exceeded (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Maximum verification attempts exceeded",
  "action_time": "2025-01-24T10:30:45",
  "data": "Maximum verification attempts exceeded"
}

3. Send OTP to Channel

Purpose: Send OTP to a specific channel when user has multiple options

Endpoint: POST {base_url}/auth/send-otp-to-channel

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "channel": "EMAIL"
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
tempTokenstringYesToken from /auth/startValid JWT
channelstringYesPreferred OTP channelenum: EMAIL, PHONE

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "RegistrationOTP completed successfully. You are now logged in."sent",
  "action_time": "2025-09-23T10:35:00"01-24T10:30:45",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "sentTo": "EMAIL",
    "maskedDestination": "jโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข@gโ€ขโ€ขโ€ขโ€ข.com",
    "expiresIn": 600,
    "message": "OTP sent to email"
  }
}

4. Login with Password

Purpose: Authenticate using password instead of OTP

Endpoint: POST {base_url}/auth/login/password

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "password": "mySecurePassword123",
  "deviceId": "a1b2c3d4e5f6789",
  "deviceName": "Chrome on macOS",
  "platform": "WEB"
}

Success Response JSON Sample (Known Device):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "userData"systemUsername": "su_550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe99",
    "displayName": "John Doe",
    "accountTier": "FULL",
    "newDevice": false,
    "requiresDeviceVerification": false,
    "message": "Login successful"
  }
}

Success Response JSON Sample (New Device - Verification Required):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "id"newDevice": true,
    "requiresDeviceVerification": true,
    "deviceVerificationToken": "123e4567-e89b-12d3-a456-426614174000"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "userName"requiresChannelSelection": false,
    "otpSentTo": "johndoe-a1B2c3D"โ€ขโ€ขโ€ข โ€ขโ€ขโ€ข โ€ขโ€ข78",
    "message": "Device verification required. OTP sent."
  }
}

Error Responses:

Invalid Password (403):

{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Invalid password",
  "action_time": "2025-01-24T10:30:45",
  "data": "Invalid password"
}

5. OAuth Login (Google)

Purpose: Authenticate using Google OAuth2 authorization code flow

Endpoint: POST {base_url}/auth/login/oauth

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "provider": "GOOGLE",
  "code": "4/0AX4XfWh...",
  "redirectUri": "https://app.nextgate.com/auth/callback",
  "state": "random-state-string",
  "deviceId": "a1b2c3d4e5f6789",
  "deviceName": "Chrome on macOS",
  "platform": "WEB"
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
providerstringYesOAuth providerenum: GOOGLE, APPLE
codestringYesAuthorization codeNon-empty
redirectUristringYesRedirect URIMust match OAuth config
statestringNoState for CSRF protection-
deviceIdstringNoDevice fingerprint-
deviceNamestringNoDevice name-
platformstringNoDevice platformenum: IOS, ANDROID, WEB

Success Response JSON Sample (New User):

{
  "success": true,
  "httpStatus": "OK",
  "message": "OAuth login processed",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "newUser": true,
    "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "currentStep": "OTP_VERIFIED",
    "nextStep": "AGE_VERIFIED",
    "nextStepMetadata": {
      "firstName": "John",
      "lastName": "Doe",
      "profilePicture": "https://lh3.googleusercontent.com/a/...",
      "hasOAuthData": true
    },
    "message": "Complete your registration",
    "state": "random-state-string"
  }
}

Success Response JSON Sample (Existing User):

{
  "success": true,
  "httpStatus": "OK",
  "message": "OAuth login processed",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "newUser": false,
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "systemUsername": "su_550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe99",
    "displayName": "John Doe",
    "accountTier": "FULL",
    "message": "Login successful",
    "state": "random-state-string"
  }
}

6. Verify Device

Purpose: Verify a new device using OTP

Endpoint: POST {base_url}/auth/device/verify

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "deviceVerificationToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "otp": "123456",
  "deviceId": "a1b2c3d4e5f6789",
  "deviceName": "Chrome on macOS",
  "platform": "WEB"
}

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Device verified",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "systemUsername": "su_550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe99",
    "displayName": "John Doe",
    "accountTier": "FULL",
    "message": "Login successful"
  }
}

7. Resend OTP

Purpose: Resend OTP code (with rate limiting)

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

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP resend processed",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "maskedDestination": "โ€ขโ€ขโ€ข โ€ขโ€ขโ€ข โ€ขโ€ข78",
    "sentTo": "PHONE",
    "expiresIn": 600,
    "attemptsRemaining": 4,
    "cooldownSeconds": 0,
    "message": "OTP resent"
  }
}

Cooldown Response:

{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP resend processed",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "cooldownSeconds": 45,
    "attemptsRemaining": 3,
    "message": "Please wait 45 seconds before requesting again"
  }
}

Onboarding Endpoints

8. Set Age (Step 1)

Purpose: Verify user's age and save first/last name

Endpoint: POST {base_url}/auth/onboarding/age

Access Level: ๐ŸŒ Public (requires valid onboarding token)

Authentication: None (onboarding token in body)

Request JSON Sample:

{
  "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "firstName": "John",
  "lastName": "Doe",
  "middleName"birthDate": "Michael",
      "email": "john.doe@example.com",
      "isVerified": true,
      "isEmailVerified": true,
      "isPhoneVerified": false,
      "roles": ["ROLE_NORMAL_USER"],
      "createdAt": "2025-09-23T10:30:00",
      "editedAt": "2025-09-23T10:35:00"
    }
  }1999-05-15"
}

ResponseRequest FieldsBody Parameters:

FieldParameterTypeRequiredDescriptionValidation
onboardingTokenstringYesToken from previous stepValid JWT
firstNamestringYesUser's first name1-50 characters
lastNamestringYesUser's last name1-50 characters
birthDatestringYesDate of birthISO date (YYYY-MM-DD), must be in past

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Age verified",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "accountTier": "FULL",
    "age": 25,
    "restricted": false,
    "blocked": false,
    "currentStep": "AGE_VERIFIED",
    "nextStep": "USERNAME_SET",
    "nextStepMetadata": {
      "suggestedUsernames": [
        "johndoe99",
        "john_doe_official",
        "jdoe_tz",
        "the_johndoe",
        "johnd_pro"
      ]
    },
    "message": "Age verified successfully"
  }
}

Blocked Response (Under 13):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Age verified",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "accountTier": null,
    "age": 12,
    "restricted": true,
    "blocked": true,
    "currentStep": "AGE_VERIFIED",
    "nextStep": null,
    "message": "You must be at least 13 years old to use NextGate"
  }
}

9. Check Username Availability

Purpose: Check if a username is available (for real-time validation)

Endpoint: POST {base_url}/auth/onboarding/check-username

Access Level: ๐ŸŒ Public

Authentication: None

Request JSON Sample:

{
  "username": "johndoe99"
}

Success Response JSON Sample (Available):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Username availability checked",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "username": "johndoe99",
    "available": true
  }
}

Success Response JSON Sample (Not Available):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Username not available",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "username": "johndoe",
    "available": false,
    "suggestions": ["johndoe99", "johndoe_official", "the_johndoe"]
  }
}

10. Set Username (Step 2)

Purpose: Set the user's chosen username

Endpoint: POST {base_url}/auth/onboarding/username

Access Level: ๐ŸŒ Public (requires valid onboarding token)

Authentication: None (onboarding token in body)

Request JSON Sample:

{
  "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "username": "johndoe99"
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
onboardingTokenstringYesToken from previous stepValid JWT
usernamestringYesChosen username3-30 chars, starts with letter, alphanumeric + underscore

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Username set",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "username": "johndoe99",
    "available": true,
    "currentStep": "USERNAME_SET",
    "nextStep": "INTERESTS_SELECTED",
    "message": "Username set successfully"
  }
}

11. Get Interest Categories

Purpose: Get all available interest categories for selection

Endpoint: GET {base_url}/interests/categories/all

Access Level: ๐ŸŒ Public

Authentication: None

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Categories retrieved",
  "action_time": "2025-01-24T10:30:45",
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Music",
      "icon": "๐ŸŽต",
      "description": "Concerts, artists, playlists",
      "displayOrder": 1,
      "isActive": true
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "name": "Sports",
      "icon": "โšฝ",
      "description": "Games, teams, fitness",
      "displayOrder": 2,
      "isActive": true
    }
  ]
}

12. Set Interests (Step 3)

Purpose: Save user's selected interests

Endpoint: POST {base_url}/auth/onboarding/interests

Access Level: ๐ŸŒ Public (requires valid onboarding token)

Authentication: None (onboarding token in body)

Request JSON Sample:

{
  "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "interestIds": [
    "550e8400-e29b-41d4-a716-446655440001",
    "550e8400-e29b-41d4-a716-446655440002",
    "550e8400-e29b-41d4-a716-446655440003"
  ]
}

Request Body Parameters:

ParameterTypeRequiredDescriptionValidation
onboardingTokenstringYesToken from previous stepValid JWT
interestIdsarrayYesList of category UUIDsMin 3 items

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Interests saved",
  "action_time": "2025-01-24T10:30:45",
  "data": {
    "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "selectedInterests": ["Music", "Sports", "Technology"],
    "count": 3,
    "currentStep": "INTERESTS_SELECTED",
    "nextStep": "PROFILE_COMPLETED",
    "nextStepMetadata": {
      "firstName": "John",
      "lastName": "Doe",
      "username": "johndoe99",
      "suggestedDisplayName": "John Doe",
      "profilePicture": "https://lh3.googleusercontent.com/a/..."
    },
    "message": "Interests saved successfully"
  }
}

13. Set Profile (Step 4 - Final)

Purpose: Complete profile setup and finish onboarding

Endpoint: POST {base_url}/auth/onboarding/profile

Access Level: ๐ŸŒ Public (requires valid onboarding token)

Authentication: None (onboarding token in body)

Request Headers:

indicatingoperation ANDROID,
HeaderTypeRequired Description
successX-Device-Id Booleanstring No Device successfingerprint
httpStatusX-Device-Name HTTP status code as string
messageNo Success message confirming registration completion
action_timeISO 8601 timestamp of response generation
data.accessTokenJWT access token for API authentication (shorter lifespan)
data.refreshTokenJWT refresh token for obtaining new access tokens (longer lifespan)
data.userData.idUnique user identifier (UUID)
data.userData.userNameAuto-generated username (email prefix + random suffix)
data.userData.firstNameUser's firstDevice name
data.userData.lastNameX-Platform User's last name
data.userData.middleNamestring User's middle name
data.userData.emailNo User'sPlatform email(IOS, address
data.userData.isVerifiedOverall account verification status
data.userData.isEmailVerifiedEmail verification status
data.userData.isPhoneVerifiedPhone verification status
data.userData.rolesArray of user roles/permissions
data.userData.createdAtAccount creation timestamp
data.userData.editedAtLast modification timestampWEB)

ErrorRequest ResponsesJSON Sample:

    {
      
  • 400 Bad Request"onboardingToken": Invalid"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "displayName": "John Doe", "bio": "Tech enthusiast | Music lover | Based in Dar es Salaam ๐Ÿ‡น๐Ÿ‡ฟ", "profilePictureUrl": "https://storage.nextgate.com/profiles/abc123.jpg" }
  • Request Body Parameters:

    ParameterTypeRequiredDescriptionValidation
    onboardingTokenstringYesToken from previous stepValid JWT
    displayNamestringYesPublic display name1-50 characters
    biostringNoUser biographyMax 160 characters
    profilePictureUrlstringNoProfile picture URLValid URL

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Welcome to NextGate!",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "systemUsername": "su_550e8400-e29b-41d4-a716-446655440000",
        "username": "johndoe99",
        "displayName": "John Doe",
        "currentStep": "COMPLETED",
        "onboardingComplete": true,
        "message": "Welcome to NextGate!"
      }
    }
    

    Token Management

    14. Refresh Access Token

    Purpose: Get a new access token using refresh token (with rotation)

    Endpoint: POST {base_url}/auth/token/refresh

    Access Level: ๐ŸŒ Public

    Authentication: None (refresh token in body)

    Request JSON Sample:

    {
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Token refreshed",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "tokenType": "Bearer",
        "expiresIn": 3600,
        "message": "Tokens refreshed successfully"
      }
    }
    

    โš ๏ธ Important: The refresh token is rotated on each use. Always store the new refreshToken from the response.

    Error Response (Token Reuse Detected):

    {
      "success": false,
      "httpStatus": "UNAUTHORIZED",
      "message": "Security alert: Token reuse detected. Please login again.",
      "action_time": "2025-01-24T10:30:45",
      "data": "Security alert: Token reuse detected. Please login again."
    }
    

    15. Revoke Token

    Purpose: Revoke a refresh token (logout)

    Endpoint: POST {base_url}/auth/token/revoke

    Access Level: ๐ŸŒ Public

    Authentication: None

    Request JSON Sample:

    {
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Token revoked successfully",
      "action_time": "2025-01-24T10:30:45",
      "data": null
    }
    

    16. Refresh Onboarding Token

    Purpose: Refresh onboarding token if it's about to expire

    Endpoint: POST {base_url}/auth/token/refresh-onboarding

    Access Level: ๐ŸŒ Public

    Authentication: None

    Request JSON Sample:

    {
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Onboarding token refreshed",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "currentStep": "USERNAME_SET",
        "nextStep": "INTERESTS_SELECTED",
        "expiresIn": 1800,
        "message": "Token refreshed successfully"
      }
    }
    

    Password Management

    17. Initiate Forgot Password

    Purpose: Start password reset flow

    Endpoint: POST {base_url}/auth/password/forgot/initiate

    Access Level: ๐ŸŒ Public

    Authentication: None

    Request JSON Sample:

    {
      "identifier": "johndoe99"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Request processed",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "maskedDestination": "jโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข@gโ€ขโ€ขโ€ขโ€ข.com",
        "sentTo": "EMAIL",
        "expiresIn": 600,
        "otpVerified": false,
        "passwordReset": false,
        "requiresChannelSelection": false,
        "message": "OTP sent to email"
      }
    }
    

    18. Verify Forgot Password OTP

    Purpose: Verify OTP codefor formatpassword orreset

    missing required fields
  • 401 Unauthorized

    Endpoint: InvalidPOST or{base_url}/auth/password/forgot/verify-otp

    expired

    Access temporaryLevel: token

  • ๐ŸŒ
  • Public

    Authentication: None

    Request JSON Sample:

    422{
      Unprocessable"tempToken": Entity"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "otp": "123456"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "OTP verified",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "resetToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "otpVerified": true,
        "passwordReset": false,
        "message": "OTP verified. Set your new password."
      }
    }
    

    19. Reset Forgotten Password

    Purpose: Set new password after OTP verification

    failed,

    Endpoint: POST {base_url}/auth/password/forgot/reset

    Access Level: ๐ŸŒ Public

    Authentication: None

    Request JSON Sample:

    {
      "resetToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "newPassword": "newSecurePassword123",
      "confirmPassword": "newSecurePassword123"
    }
    

    Request Body Parameters:

    ParameterTypeRequiredDescriptionValidation
    resetTokenstringYesToken from verify-otpValid JWT
    newPasswordstringYesNew passwordMin 8 characters
    confirmPasswordstringYesConfirmationMust match newPassword

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Password reset successfully",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "otpVerified": true,
        "passwordReset": true,
        "message": "Password reset successfully"
      }
    }
    

    โš ๏ธ Note: After password reset, all active sessions are revoked. User must login again.


    20. Change Password (Authenticated)

    Purpose: Change password for logged-in user

    Endpoint: POST {base_url}/password/change

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request Headers:

    HeaderTypeRequiredDescription
    AuthorizationstringYesBearer {accessToken}

    Request JSON Sample:

    {
      "currentPassword": "oldPassword123",
      "newPassword": "newSecurePassword456",
      "confirmPassword": "newSecurePassword456"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Password changed",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "success": true,
        "hadPassword": true,
        "message": "Password changed successfully"
      }
    }
    

    21. Set Password (for Passwordless Users)

    Purpose: Set password for users who registered via OAuth or OTP only

    Endpoint: POST {base_url}/password/set

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request JSON Sample:

    {
      "newPassword": "myNewPassword123",
      "confirmPassword": "myNewPassword123"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Password set successfully",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "success": true,
        "hadPassword": false,
        "message": "Password set successfully. You can now login with password."
      }
    }
    

    Session Management

    22. Get All Sessions

    Purpose: List all active sessions for the user

    Endpoint: GET {base_url}/auth/sessions

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request Headers:

    HeaderTypeRequiredDescription
    AuthorizationstringYesBearer {accessToken}
    X-Session-IdstringNoCurrent session ID

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Sessions retrieved",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "sessions": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440001",
            "deviceId": "a1b2c3d4e5f6789",
            "deviceName": "Chrome on macOS",
            "platform": "WEB",
            "ipAddress": "192.168.1.1",
            "location": "Dar es Salaam, Tanzania",
            "lastActiveAt": "2025-01-24T10:30:45",
            "createdAt": "2025-01-20T08:00:00",
            "currentSession": true
          },
          {
            "id": "550e8400-e29b-41d4-a716-446655440002",
            "deviceId": "xyz789abc123",
            "deviceName": "Safari on iPhone",
            "platform": "IOS",
            "ipAddress": "192.168.1.2",
            "location": "Arusha, Tanzania",
            "lastActiveAt": "2025-01-23T15:20:00",
            "createdAt": "2025-01-15T12:00:00",
            "currentSession": false
          }
        ],
        "totalCount": 2
      }
    }
    

    23. Sign Out (Current Session)

    Purpose: End the current session

    Endpoint: POST {base_url}/auth/sessions/sign-out

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request JSON Sample:

    {
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Signed out successfully",
      "action_time": "2025-01-24T10:30:45",
      "data": null
    }
    

    24. Sign Out All Sessions

    Purpose: End all sessions including current (security logout)

    Endpoint: POST {base_url}/auth/sessions/sign-out-all

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "All sessions terminated",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "terminatedCount": 4,
        "message": "All 4 sessions have been terminated. Please login again."
      }
    }
    

    25. Revoke Specific Session

    Purpose: End a specific session by ID

    Endpoint: DELETE {base_url}/auth/sessions/{sessionId}

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Path Parameters:

    ParameterTypeRequiredDescription
    sessionIdstringYesSession UUID to revoke

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Session revoked",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "sessionId": "550e8400-e29b-41d4-a716-446655440002",
        "message": "Session has been terminated"
      }
    }
    

    Device Management

    26. Get All Devices

    Purpose: List all registered/trusted devices

    Endpoint: GET {base_url}/auth/devices

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request Headers:

    HeaderTypeRequiredDescription
    AuthorizationstringYesBearer {accessToken}
    X-Device-IdstringNoCurrent device ID

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Devices retrieved",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "devices": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440001",
            "deviceId": "a1b2c3d4e5f6789",
            "deviceName": "Chrome on macOS",
            "platform": "WEB",
            "lastIpAddress": "192.168.1.1",
            "lastLocation": "Dar es Salaam, Tanzania",
            "lastActiveAt": "2025-01-24T10:30:45",
            "firstSeenAt": "2025-01-01T08:00:00",
            "trustLevel": "TRUSTED",
            "isCurrentDevice": true
          }
        ],
        "totalCount": 1
      }
    }
    

    27. Remove Device

    Purpose: Remove a device from trusted devices (revokes all sessions on that device)

    Endpoint: DELETE {base_url}/auth/devices/{deviceId}

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Path Parameters:

    ParameterTypeRequiredDescription
    deviceIdstringYesDevice record UUID

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Device removed",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "deviceId": "550e8400-e29b-41d4-a716-446655440002",
        "sessionsRevoked": 2,
        "message": "Device removed and 2 sessions terminated"
      }
    }
    

    Account Linking

    28. Get Linked Accounts

    Purpose: List all linked OAuth providers and identifiers

    Endpoint: GET {base_url}/auth/linked-accounts

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Linked accounts retrieved",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "linkedAccounts": [
          {
            "type": "EMAIL",
            "value": "john.doe@gmail.com",
            "isPrimary": true,
            "isVerified": true,
            "linkedAt": "2025-01-01T08:00:00"
          },
          {
            "type": "PHONE",
            "value": "+255712345678",
            "isPrimary": false,
            "isVerified": true,
            "linkedAt": "2025-01-05T10:00:00"
          },
          {
            "type": "OAUTH",
            "provider": "GOOGLE",
            "email": "john.doe@gmail.com",
            "linkedAt": "2025-01-01T08:00:00"
          }
        ],
        "hasPassword": true,
        "canRemoveEmail": true,
        "canRemovePhone": true
      }
    }
    

    Purpose: Add a new email to the account

    Endpoint: POST {base_url}/auth/linked-accounts/email/link

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request JSON Sample:

    {
      "email": "john.work@company.com"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Verification email sent",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "email": "jโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข@cโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข.com",
        "expiresIn": 600,
        "message": "Verification code sent"
      }
    }
    

    Purpose: Add a new phone number to the account

    Endpoint: POST {base_url}/auth/linked-accounts/phone/link

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request JSON Sample:

    {
      "phone": "+255787654321"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "Verification SMS sent",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "phone": "โ€ขโ€ขโ€ข โ€ขโ€ขโ€ข โ€ขโ€ข21",
        "expiresIn": 600,
        "message": "Verification code sent"
      }
    }
    

    Endpoint: POST {base_url}/auth/linked-accounts/oauth/link

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Request JSON Sample:

    {
      "provider": "APPLE",
      "code": "authorization_code_from_apple",
      "redirectUri": "https://app.nextgate.com/auth/callback"
    }
    

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "OAuth provider linked",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "provider": "APPLE",
        "email": "john.doe@icloud.com",
        "linkedAt": "2025-01-24T10:30:45",
        "message": "Apple account linked successfully"
      }
    }
    

    Purpose: Remove an OAuth provider from the account

    Endpoint: DELETE {base_url}/auth/linked-accounts/oauth/{provider}

    Access Level: ๐Ÿ”’ Protected

    Authentication: Bearer Token

    Path Parameters:

    ParameterTypeRequiredDescription
    providerstringYesGOOGLE or APPLE

    Success Response JSON Sample:

    {
      "success": true,
      "httpStatus": "OK",
      "message": "OAuth provider unlinked",
      "action_time": "2025-01-24T10:30:45",
      "data": {
        "provider": "APPLE",
        "message": "Apple account unlinked"
      }
    }
    

    Error Response (Last Login Method):

    {
      "success": false,
      "httpStatus": "BAD_REQUEST",
      "message": "Cannot unlink: This is your only login method.",
      "action_time": "2025-01-24T10:30:45",
      "data": "Cannot unlink: This is your only login method."
    }
    

    Error Reference

    Standard Error Codes

    tokenalreadyused
  • 500
  • Internal Server Error: Server-side processing errors

    Quick Reference Guide

    Common HTTP Status Codes

    • 200 OK: Successful GET/PUT/PATCH request
    • 201 Created: Successful POST request
    • 204 No Content: Successful DELETE request
    • 400 Bad Request: Invalid request data
    • 401 Unauthorized: Authentication required/failed
    • 403 Forbidden: Insufficient permissions
    • 404 Not Found:
  • UnprocessableEntity:
  • TooMany Requests:
    HTTP CodeStatusCommon Causes
    400BAD_REQUESTInvalid request data, item already exists
    401UNAUTHORIZEDMissing/invalid/expired token
    403FORBIDDENInvalid OTP, max attempts exceeded, orblocked
    404 NOT_FOUND Resource notdoesn't foundexist
    422 UNPROCESSABLE_ENTITY Validation errors
    429 TOO_MANY_REQUESTS Rate limit exceeded
    500INTERNAL_SERVER_ERRORServer error

    Authentication-Specific Errors

    Error MessageHTTP CodeResolution
    "Token has expired"401Refresh token or re-authenticate
    "Invalid OTP code"403Re-enter correct code
    "Maximum verification attempts exceeded"403Request new OTP
    "This phone is blocked"400Contact support
    "Account not found"404Check identifier
    "Security alert: Token reuse detected"401Login again
    "Login blocked: Too many failed attempts"400Wait or reset password
    "You must be at least 13 years old"N/ACannot use service

    Frontend Integration Checklist

    Before Starting

    •  Install FingerprintJS: npm install @fingerprintjs/fingerprintjs
    • 500 InternalSet Serverup Error:secure Servertoken errorstorage (httpOnly cookies or secure storage)
    •  Configure API base URL

    Authentication Types

    Flow
    • Bearer Token:Implement Includedevice fingerprint generation on app load
    •  Handle all response types from Authorization:/auth/start
    • Bearer
    • your_access_tokenImplement OTP input with 6-digit validation
    •  Handle device verification flow
    •  Store tokens securely after successful auth

    Onboarding Flow

    •  Pre-fill forms using nextStepMetadata when available
    •  Display username suggestions as clickable chips
    •  Implement real-time username availability check (debounced)
    •  Load interest categories from API
    •  Require minimum 3 interests
    •  Handle profile picture upload

    Token Management

    •  Implement automatic token refresh before expiry
    •  Handle 401 responses globally (redirect to login)
    •  Store new refresh token after each rotation
    •  Clear all tokens on logout

    Security Best Practices

    1. Never store tokens in headerslocalStorage - Use httpOnly cookies or secure native storage
    2. TemporaryAlways Tokensend device fingerprint: Used- in request bodyRequired for OTPdevice verificationtrust flowstracking
    3. RefreshHandle Tokentoken rotation: Used- toAlways obtainsave new accessrefresh tokenstoken whenafter they expirerefresh
    4. NoneValidate OTP client-side: No- authenticationOnly requiredallow for6 publicdigits endpoints
    5. before
API

Data Format Standards

  • Dates: Use ISO 8601 format (2025-09-23T10:30:00Z)call
  • PhoneRate Numberslimit on frontend: E.164- internationalDisable formatresend (+1234567890)button during cooldown
  • UUIDsClear tokens on security events: Standard- UUIDToken formatreuse (123e4567-e89b-12d3-a456-426614174000)detection, password reset

  • OTPEnd Codesof Documentation:

    6-digit numeric strings
  • Tokens: JWT format with proper expiration handling