# Business & Birthdays — Mobile App Implementation Guide

> **Version:** 1.0  
> **Last Updated:** 2026-07-10  
> **Base URL:** `{{APP_URL}}/api/v1`  
> **Authentication:** Sanctum Bearer Token  
> **Target Platform:** React Native / Expo  

---

## Table of Contents

1. [API Configuration & Authentication](#1-api-configuration--authentication)
2. [Data Types Reference](#2-data-types-reference)
3. [Module 1 — Authentication](#3-module-1--authentication)
4. [Module 2 — Subscription Plans & Subscriptions](#4-module-2--subscription-plans--subscriptions)
5. [Module 3 — Login Session Tracking](#5-module-3--login-session-tracking)
6. [Module 4 — Member Portfolios](#6-module-4--member-portfolios)
7. [Module 5 — Birthday Rewards](#7-module-5--birthday-rewards)
8. [Module 6 — Content Submission & Review](#8-module-6--content-submission--review)
9. [Module 7 — Advertisements](#9-module-7--advertisements)
10. [Module 8 — Wallet & Earnings](#10-module-8--wallet--earnings)
11. [Module 9 — Member Rankings](#11-module-9--member-rankings)
12. [Module 10 — Referral System](#12-module-10--referral-system)
13. [Module 11 — Notifications](#13-module-11--notifications)
14. [Module 12 — Admin Dashboard](#14-module-12--admin-dashboard)
15. [Deep Linking for Referrals](#15-deep-linking-for-referrals)
16. [Error Response Formats](#16-error-response-formats)

---

## 1. API Configuration & Authentication

### Base Configuration

```
Base URL: https://yourdomain.com/api/v1
```

All authenticated requests require an `Authorization: Bearer <token>` header.

### How Authentication Works

1. User registers or logs in → gets a **Sanctum token** + **user object**
2. Store the token securely (e.g., `expo-secure-store`)
3. Attach token to all subsequent API requests as `Authorization: Bearer <token>`
4. When token expires or user logs out → delete token and redirect to login

### Auth Endpoints

**All auth endpoints must be implemented in the app's first screen.**

---

## 2. Data Types Reference

These are the exact data shapes the API returns. Every mobile developer needs these for building screens.

```typescript
// ===================================================
// 2.1 User Object (returned on login, register, verify)
// ===================================================
interface User {
    id: number;
    membership_id: string | null;           // e.g., "TITAN-0042"
    first_name: string;
    last_name: string;
    phone_number: string;                   // e.g., "08123456789"
    email: string;
    email_verified_at: string | null;       // ISO datetime
    phone_verified_at: string | null;
    isPhoneVerified: boolean;
    isEmailVerified: boolean;
    profile_complete_status: boolean;
    created_at: string;                     // Relative: "3 days ago"
    roles: string[];                        // ["admin"] or ["titan_member"] or ["user"]
    permissions: string[];                  // ["navigate_application", "view_wallet", ...]
    
    // --- New Titan fields ---
    is_titan_member: boolean;
    current_rank: string | null;            // "regular" | "vip" | "vvip" | null
    wallet_balance: number;                 // e.g., 12500.00
    total_subscription_months: number;
    
    // --- Subscription ---
    subscription: {
        plan_name: string;                  // "Basic" | "Standard" | "Premium"
        plan_slug: string;                  // "basic" | "standard" | "premium"
        start_date: string;                 // "2026-07-01"
        end_date: string;                   // "2026-07-31"
        days_remaining: number;
        daily_hours_limit: number;          // 6 | 12 | 0 (0 = unlimited)
        payment_status: string;             // "paid" | "pending" | "failed"
    } | null;
    
    // --- Referral ---
    referral: {
        code: string | null;                // "JAN1A3F9K2B"
        link: string | null;                // "https://domain.com/register?ref=JAN1A3F9K2B"
        total_referrals: number;
    };
}

// ===================================================
// 2.2 Subscription Plan
// ===================================================
interface SubscriptionPlan {
    id: number;
    name: string;           // "Basic" | "Standard" | "Premium"
    slug: string;           // "basic" | "standard" | "premium"
    price: number;          // 2000 | 3500 | 5000
    daily_hours_limit: number; // 6 | 12 | 0
    duration_days: number;  // 30
    features: string[] | null;
    /*
      Basic features: ["6 hours daily access", "Portfolio creation", "Advert placement", "Birthday reward eligibility"]
      Standard: ["12 hours daily access", "Everything in Basic", "Featured listing", "Priority support"]
      Premium: ["Unlimited daily access", "Everything in Standard", "VIP support", "Exclusive features"]
    */
}

// ===================================================
// 2.3 Active Subscription (from /subscriptions/my-subscription)
// ===================================================
interface ActiveSubscription {
    has_active_subscription: boolean;
    subscription: {
        id: number;
        plan_name: string;
        plan_slug: string;
        price: number;
        start_date: string;         // "2026-07-01"
        end_date: string;           // "2026-07-31"
        days_remaining: number;
        payment_status: string;
        auto_renew: boolean;
        daily_hours: {
            limit: number;          // 6 (or 0 for unlimited)
            limit_minutes: number;  // 360
            used_minutes: number;   // 225
            remaining_minutes: number; // 135 (-1 means unlimited)
        };
    } | null;
}

// ===================================================
// 2.4 Daily Hours Status
// ===================================================
interface DailyHours {
    limit: number;              // hours limit from plan (0 = unlimited)
    limit_minutes: number;
    used_minutes: number;
    remaining_minutes: number;  // -1 means unlimited
}

// ===================================================
// 2.5 Member Portfolio
// ===================================================
interface Portfolio {
    id: number;
    business_name: string;
    business_category: string | null;   // "Fashion" | "Art" | "Photography" | etc.
    business_description: string | null;
    whatsapp_number: string | null;     // "+2348012345678"
    profile_image_url: string | null;
    cover_image_url: string | null;
    is_approved: boolean;
    is_featured: boolean;
    views_count: number;
    user?: {
        id: number;
        name: string;                   // "Jane Doe"
        profile_photo_url: string | null;
        whatsapp_number?: string | null;
    };
    items: PortfolioItem[];
    items_count?: number;               // when in list view
}

// ===================================================
// 2.6 Portfolio Item
// ===================================================
interface PortfolioItem {
    id: number;
    title: string;                      // "Women's Ankara Gown"
    description: string | null;
    price: number | null;               // 25000
    image_urls: string[] | null;        // ["https://...", "https://..."]
    whatsapp_dm_link: string | null;    // Direct WhatsApp deep link
    is_active: boolean;
    created_at?: string;                // ISO datetime (in "my" view)
}

// ===================================================
// 2.7 Birthday Reward & Eligibility
// ===================================================
interface BirthdayEligibility {
    date_of_birth: string | null;       // "1995-12-25"
    birthday_this_month: boolean;
    total_subscription_months: number;
    is_eligible_for_reward: boolean;    // true if >= 8 months
    is_shoutout_only: boolean;          // true if < 8 months
    current_reward: {
        id: number;
        status: string;                 // "pending" | "processing" | "delivered" | "cancelled"
        reward_type: string;            // "physical_delivery" | "service" | "shoutout"
        is_shoutout_only: boolean;
    } | null;
}

interface UpcomingBirthday {
    user_id: number;
    name: string;
    profile_photo_url: string | null;
    birth_date: string;                 // "2026-07-15"
    age: number;
    days_until_birthday: number;
    rank: string | null;                // "regular" | "vip" | "vvip"
    total_subscription_months: number;
    business: {
        name: string | null;
        category: string | null;
    } | null;
}

interface BirthdayReward {
    id: number;
    user: { id: number; name: string; email: string; } | null;
    reward_type: string;
    status: string;
    reward_month: string;               // "2026-07"
    is_shoutout_only: boolean;
    gift_provider: string | null;       // "John Smith" (name)
    gift_item: string | null;           // "Ankara Gown" (title)
    created_at: string | null;
}

// ===================================================
// 2.8 Content Submission
// ===================================================
interface ContentSubmission {
    id: number;
    title: string;
    content_type: string;               // "advertisement" | "promotional_video" | "graphic" | "photo"
    status: string;                     // "pending_review" | "approved" | "rejected"
    media_url: string;
    duration_seconds: number | null;
    submitted_at: string;
    reviewed_at: string | null;
    rejection_reason: string | null;
    estimated_review_completion: string; // ISO datetime (submitted_at + 72h)
}

// ===================================================
// 2.9 Wallet Transaction
// ===================================================
interface WalletTransaction {
    id: number;
    type: string;                       // "referral_bonus" | "subscription_payment" | "birthday_reward" | "withdrawal" | "ad_payment" | "admin_adjustment"
    amount: number;                     // Positive = credit, Negative = debit
    balance_before: number;
    balance_after: number;
    reference: string;                  // "WDL-ABC123"
    description: string | null;
    status: string;                     // "pending" | "completed" | "failed"
    created_at: string;                 // ISO datetime
}

// ===================================================
// 2.10 Member Ranking
// ===================================================
interface MemberRanking {
    current_rank: string;               // "regular" | "vip" | "vvip"
    total_months_subscribed: number;
    consecutive_months: number;
    rank_achieved_at: string | null;
    next_rank_info: {
        next_rank: string | null;       // "vip" | "vvip" | null
        months_remaining: number | null;
        progress_percentage: number;    // 0-100
    };
}

interface LeaderboardEntry {
    user_id: number;
    name: string;
    email: string;
    profile_photo_url: string | null;
    rank: string;
    total_months: number;
}

// ===================================================
// 2.11 Advertisement
// ===================================================
interface Advertisement {
    id: number;
    title: string;
    description: string | null;
    media_url: string | null;
    target_url: string | null;
    placement: string;                  // "homepage" | "catalogue_page" | "sidebar"
    advertiser: string;                 // "Jane Doe"
    start_date?: string;
    end_date?: string;
    status?: string;
}

// ===================================================
// 2.12 Referral Stats
// ===================================================
interface ReferralStats {
    total_referrals: number;
    active_referrals: number;
    total_earnings: number;
    pending_rewards: number;
    available_balance: number;
}

// ===================================================
// 2.13 Notification
// ===================================================
interface AppNotification {
    id: number;
    type: string;                       // "transaction" | "system" | "promotion" | "update" | "alert"
    title: string;
    body: string;
    data: object | null;
    priority: string;                   // "low" | "normal" | "high"
    read_at: string | null;
    created_at: string;
    time_ago: string;                   // "2 hours ago"
}

// ===================================================
// 2.14 Generic Paginated Response
// ===================================================
interface PaginatedResponse<T> {
    data: T[];
    pagination: {
        current_page: number;
        per_page: number;
        total: number;
        last_page: number;
        from: number | null;
        to: number | null;
    };
}
```

---

## 3. Module 1 — Authentication

### Screens Needed
- Login Screen
- Register Screen
- Email Verification (OTP) Screen
- Forgot Password Screen
- Reset Password Screen

### Endpoints

#### 3.1 Register

```
POST /auth/register
Content-Type: application/json

Body:
{
    "first_name": "Jane",              // required, string
    "last_name": "Doe",                // required, string
    "phone_number": "+2348012345678",  // required, Nigerian format
    "email": "jane@example.com",       // required, valid email
    "password": "Password123!",        // required, min 8 chars, mixed case, numbers, symbols
    "password_confirmation": "Password123!", // required, must match password
    "ref": "JOH5A3F9K2B"              // optional, referral code from URL
}

Success 201:
{
    "success": true,
    "message": "User registered successfully. Please verify your email.",
    "data": {
        "user": User,
        "token": "1|sanctum_token_string",
        "email_verification_sent": true
    }
}

Error 422 (validation):
{
    "success": false,
    "message": "The email has already been taken.",
    "data": { "email": ["The email has already been taken."] },
    "code": 422
}
```

**Phone number format:** The backend accepts both `+2348012345678` (14 chars) and `08123456789` (11 chars). It normalizes to `08123456789` format internally.

**On mobile:** The `ref` parameter comes from the deep link when a user taps a referral link. Extract `?ref=CODE` from the URL when the app opens via deep link.

#### 3.2 Login

```
POST /auth/login
Content-Type: application/json

Body:
{
    "email": "jane@example.com",   // required, valid email
    "password": "Password123!"     // required
}

Success 200:
{
    "success": true,
    "message": "Login successful",
    "data": {
        "token": "1|sanctum_token_string",
        "user": User,
        "login_channel": "mobile",
        "email_verified": true
    }
}

Error 401:
{
    "success": false,
    "message": "Invalid credentials",
    "code": 401
}
```

**On mobile:** Send `location` object optionally with `lat`, `lng`, and `accuracy` fields. This gets stored for location analytics.

```json
{
    "email": "jane@example.com",
    "password": "Password123!",
    "location": {
        "lat": 6.5243793,
        "lng": 3.3792057,
        "accuracy": 20
    }
}
```

#### 3.3 Verify Email (OTP)

```
POST /auth/verify-email-with-otp
Content-Type: application/json

Body:
{
    "email": "jane@example.com",   // required, must exist in DB
    "otp": "482915"                // required, exactly 6 digits
}

Success 200:
{
    "success": true,
    "message": "Email verified successfully",
    "data": { "user": User }
}

Error 400:
{
    "success": false,
    "message": "OTP has expired. Please request a new one",
    "code": 400
}
```

#### 3.4 Resend Email Verification OTP

```
POST /auth/resend-email-verification-otp
Content-Type: application/json

Body:
{
    "email": "jane@example.com"
}

Success 200:
{
    "success": true,
    "message": "Verification OTP sent successfully",
    "data": []
}

Error 429 (rate limited):
{
    "success": false,
    "message": "Please wait before requesting a new OTP",
    "code": 429
}
```

#### 3.5 Forgot Password

```
POST /auth/forgot-password
Content-Type: application/json

Body:
{
    "email": "jane@example.com"
}

Success 200:
{
    "success": true,
    "message": "Password reset OTP sent successfully",
    "data": { "email": "jane@example.com" }
}
```

#### 3.6 Verify Password Reset OTP

```
POST /auth/verify-password-reset-otp
Content-Type: application/json

Body:
{
    "email": "jane@example.com",
    "otp": "729104"
}

Success 200:
{
    "success": true,
    "message": "OTP verified successfully. You can now reset your password.",
    "data": {
        "email": "jane@example.com",
        "reset_token": "a1b2c3d4e5f6..."  // 64-char hex string
    }
}
```

#### 3.7 Reset Password

```
POST /auth/reset-password
Content-Type: application/json

Body:
{
    "email": "jane@example.com",
    "reset_token": "a1b2c3d4e5f6...",  // from verify-otp response
    "password": "NewPass123!",
    "password_confirmation": "NewPass123!"
}

Success 200:
{
    "success": true,
    "message": "Password reset successfully. Please login with your new password.",
    "data": []
}
```

#### 3.8 Logout

```
POST /auth/logout
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Logged out successfully",
    "data": []
}
```

#### 3.9 Get Authenticated User

```
GET /auth/user
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "User retrieved successfully",
    "data": { "user": User }
}
```

#### 3.10 Verify Token

```
GET /auth/verify
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Token is valid",
    "data": {
        "valid": true,
        "user": User
    }
}

Error 401:
{
    "success": false,
    "message": "Unauthenticated",
    "code": 401
}
```

**Use case:** Call this on app launch to check if stored token is still valid.

#### 3.11 Update Profile

```
PUT /auth/profile
Authorization: Bearer <token>
Content-Type: application/json

Body (camelCase or snake_case — both accepted):
{
    "basicInfo": {
        "firstName": "Jane",
        "lastName": "Smith",
        "email": "jane@example.com",
        "dob": "1990-05-15",
        "gender": "female"
    },
    "identityInfo": {
        "nin": "12345678901",
        "phone": "+2348012345678"
    },
    "bankInfo": {
        "account_number": "0123456789",
        "account_name": "Jane Doe",
        "bank_code": "058",
        "bank_name": "GTBank",
        "bvn": "12345678901"
    }
}

Success 200:
{
    "success": true,
    "message": "Profile updated successfully",
    "data": { ... }
}
```

#### 3.12 Change Password

```
POST /auth/change-password
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "current_password": "OldPass123!",
    "password": "NewPass456!",
    "password_confirmation": "NewPass456!"
}

Success 200:
{
    "success": true,
    "message": "Password changed successfully. Please use the new token for subsequent requests.",
    "data": { "token": "2|new_token_string" }
}
```

**Important:** This revokes all existing tokens and returns a NEW token. The app must replace the stored token.

#### 3.13 Delete Account

```
POST /auth/delete-account
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "password": "CurrentPass123!",
    "reason": "I no longer need the service"   // optional
}

Success 200:
{
    "success": true,
    "message": "Your account has been successfully deleted. Your data will be permanently removed after 30 days.",
    "data": []
}
```

#### 3.14 Update Location

```
POST /auth/update-location
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "latitude": 6.5243793,
    "longitude": 3.3792057,
    "accuracy": 20.5
}
```

---

## 4. Module 2 — Subscription Plans & Subscriptions

### Screens Needed
- Subscription Plans Screen (list all available plans with pricing)
- My Subscription Screen (show current plan, days remaining, daily hours usage)
- Subscription History Screen

### Endpoints

#### 4.1 Get All Subscription Plans (Public)

```
GET /subscriptions/plans

Success 200:
{
    "success": true,
    "message": "Plans retrieved successfully",
    "data": {
        "plans": [
            {
                "id": 1,
                "name": "Basic",
                "slug": "basic",
                "price": 2000,
                "daily_hours_limit": 6,
                "duration_days": 30,
                "features": [
                    "6 hours daily access",
                    "Portfolio creation",
                    "Advert placement",
                    "Birthday reward eligibility"
                ]
            },
            {
                "id": 2,
                "name": "Standard",
                "slug": "standard",
                "price": 3500,
                "daily_hours_limit": 12,
                "duration_days": 30,
                "features": [
                    "12 hours daily access",
                    "Everything in Basic",
                    "Featured listing",
                    "Priority support"
                ]
            },
            {
                "id": 3,
                "name": "Premium",
                "slug": "premium",
                "price": 5000,
                "daily_hours_limit": 0,
                "duration_days": 30,
                "features": [
                    "Unlimited daily access",
                    "Everything in Standard",
                    "VIP support",
                    "Exclusive features"
                ]
            }
        ]
    }
}
```

**Note:** `daily_hours_limit: 0` means unlimited. Display as "Unlimited" in the UI.

#### 4.2 Subscribe to a Plan

```
POST /subscriptions/subscribe
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "plan_id": 2,               // required — the ID from the plans list
    "payment_reference": "PAY-REF-12345",  // optional — from payment gateway
    "auto_renew": true          // optional, default false
}

Success 201:
{
    "success": true,
    "message": "Subscription created successfully",
    "data": {
        "subscription": {
            "id": 15,
            "plan": "Standard",
            "start_date": "2026-07-10",
            "end_date": "2026-08-09",
            "payment_status": "paid",
            "days_remaining": 30
        }
    }
}

Error 400 (already has active sub):
{
    "success": false,
    "message": "You already have an active subscription. Cancel it first or wait for it to expire.",
    "code": 400
}

Error 403 (no active sub — shouldn't happen here):
{
    "success": false,
    "message": "No active subscription. Please subscribe to a plan.",
    "code": 403
}
```

**Integration flow:**
1. User selects a plan → show payment gateway (Paystack/Flutterwave)
2. Payment gateway returns a `payment_reference`
3. Send that reference to this endpoint
4. On success → redirect to dashboard

#### 4.3 Get My Active Subscription

```
GET /subscriptions/my-subscription
Authorization: Bearer <token>

Success 200 (has subscription):
{
    "success": true,
    "message": "Subscription retrieved successfully",
    "data": {
        "has_active_subscription": true,
        "subscription": {
            "id": 15,
            "plan_name": "Standard",
            "plan_slug": "standard",
            "price": 3500,
            "start_date": "2026-07-10",
            "end_date": "2026-08-09",
            "days_remaining": 30,
            "payment_status": "paid",
            "auto_renew": false,
            "daily_hours": {
                "limit": 12,
                "limit_minutes": 720,
                "used_minutes": 225,
                "remaining_minutes": 495
            }
        }
    }
}

Success 200 (no subscription):
{
    "success": true,
    "message": "No active subscription",
    "data": {
        "has_active_subscription": false,
        "subscription": null
    }
}
```

**Important:** Use the `daily_hours` data to show a visual progress bar of today's usage. If `remaining_minutes` is -1, show "Unlimited".

#### 4.4 Get Subscription History

```
GET /subscriptions/history?page=1&per_page=20
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "subscriptions": [
            {
                "id": 15,
                "plan": "Standard",
                "start_date": "2026-07-10",
                "end_date": "2026-08-09",
                "amount": 3500,
                "payment_status": "paid",
                "is_active": true,
                "created_at": "2026-07-10 10:30:00"
            },
            {
                "id": 10,
                "plan": "Basic",
                "start_date": "2026-06-01",
                "end_date": "2026-07-01",
                "amount": 2000,
                "payment_status": "paid",
                "is_active": false,
                "created_at": "2026-06-01 08:15:00"
            }
        ],
        "pagination": { ... }
    }
}
```

#### 4.5 Cancel Auto-Renewal

```
POST /subscriptions/cancel-auto-renew
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Auto-renewal cancelled successfully",
    "data": {
        "subscription_id": 15,
        "auto_renew": false
    }
}
```

#### 4.6 Get Remaining Hours

```
GET /subscriptions/remaining-hours
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Remaining hours retrieved",
    "data": {
        "daily_hours": {
            "limit": 12,
            "limit_minutes": 720,
            "used_minutes": 225,
            "remaining_minutes": 495
        }
    }
}
```

**Use case:** Poll this endpoint periodically while the app is active to update the usage counter.

---

## 5. Module 3 — Login Session Tracking

### How It Works

The app must track how long the user is actively using the app. This enforces the subscription plan's daily hour limit.

**Flow:**
1. App comes to foreground → `POST /sessions/start` → get session ID
2. App goes to background → `POST /sessions/end { session_id }` → records duration
3. Periodically check `GET /sessions/remaining` to display remaining time
4. If `remaining_minutes` reaches 0 (and plan has a limit), show a warning

### Endpoints

#### 5.1 Start Session

```
POST /sessions/start
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "device_info": "iPhone 15 Pro, iOS 18.0"  // optional
}

Success 200:
{
    "success": true,
    "message": "Session started successfully",
    "data": {
        "session_id": 52,
        "started_at": "2026-07-10 10:30:00"
    }
}

Success 200 (already has active session — return existing):
{
    "success": true,
    "message": "Session already active",
    "data": {
        "session_id": 51,
        "started_at": "2026-07-10 10:15:00",
        "elapsed_minutes": 15
    }
}

Error 403 (no subscription):
{
    "success": false,
    "message": "No active subscription. Please subscribe to a plan.",
    "code": 403
}

Error 403 (daily limit reached):
{
    "success": false,
    "message": "You have reached your daily hour limit of 6 hours.",
    "code": 403
}
```

**Important:** Call this when the app comes to the foreground or when the user logs in.

#### 5.2 End Session

```
POST /sessions/end
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "session_id": 52   // required — from start session response
}

Success 200:
{
    "success": true,
    "message": "Session ended successfully",
    "data": {
        "session_id": 52,
        "duration_minutes": 45,
        "daily_hours": {
            "limit": 12,
            "limit_minutes": 720,
            "used_minutes": 270,
            "remaining_minutes": 450
        },
        "warning": null    // or "You have reached your daily hour limit..."
    }
}
```

**Important:** Call this when the app goes to background or user logs out.

#### 5.3 Get Today's Sessions

```
GET /sessions/today
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Today sessions retrieved",
    "data": {
        "daily_hours": {
            "limit": 12,
            "limit_minutes": 720,
            "used_minutes": 270,
            "remaining_minutes": 450
        },
        "sessions": [
            {
                "id": 51,
                "login_at": "2026-07-10T08:00:00.000000Z",
                "logout_at": "2026-07-10T08:30:00.000000Z",
                "duration_minutes": 30,
                "is_active": false
            },
            {
                "id": 52,
                "login_at": "2026-07-10T10:15:00.000000Z",
                "logout_at": null,
                "duration_minutes": null,
                "is_active": true
            }
        ]
    }
}
```

#### 5.4 Get Remaining Time

```
GET /sessions/remaining
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Remaining time retrieved",
    "data": {
        "daily_hours": {
            "limit": 12,
            "limit_minutes": 720,
            "used_minutes": 270,
            "remaining_minutes": 450
        }
    }
}
```

---

## 6. Module 4 — Member Portfolios

### Screens Needed
- Browse Catalogues Screen (grid/list of all approved portfolios)
- Single Portfolio Screen (portfolio details + items)
- My Portfolio Screen (CRUD own portfolio)
- Add/Edit Portfolio Item Screen

### Endpoints

#### 6.1 Browse All Portfolios (Public)

```
GET /portfolios?category=Fashion&search=&page=1&per_page=20

Query params: category (optional), search (optional), page, per_page

Success 200:
{
    "success": true,
    "data": {
        "portfolios": [
            {
                "id": 1,
                "business_name": "Jane's Fashion House",
                "business_category": "Fashion",
                "business_description": "We sell high quality Ankara wears...",
                "whatsapp_number": "+2348012345678",
                "profile_image_url": "https://...",
                "cover_image_url": "https://...",
                "is_featured": true,
                "views_count": 245,
                "user": {
                    "id": 5,
                    "name": "Jane Doe",
                    "profile_photo_url": "https://..."
                },
                "items_count": 8,
                "created_at": "2026-06-15 10:30:00"
            }
        ],
        "pagination": { ... }
    }
}
```

**Sorting:** Results are sorted by featured first, then by views count descending.

#### 6.2 Get Business Categories (Public)

```
GET /portfolios/categories

Success 200:
{
    "success": true,
    "data": {
        "categories": ["Fashion", "Art", "Photography", "Tech Solutions", "Catering"]
    }
}
```

**Use case:** Populate a category filter dropdown/chips.

#### 6.3 View Single Portfolio (Public)

```
GET /portfolios/{id}

Success 200:
{
    "success": true,
    "data": {
        "portfolio": {
            "id": 1,
            "business_name": "Jane's Fashion House",
            "business_category": "Fashion",
            "business_description": "We sell high quality Ankara wears and accessories...",
            "whatsapp_number": "+2348012345678",
            "profile_image_url": "https://...",
            "cover_image_url": "https://...",
            "is_featured": true,
            "views_count": 246,
            "user": {
                "id": 5,
                "name": "Jane Doe",
                "profile_photo_url": "https://...",
                "whatsapp_number": "+2348012345678"
            },
            "items": [
                {
                    "id": 10,
                    "title": "Women's Ankara Gown",
                    "description": "Beautiful handmade Ankara gown...",
                    "price": 25000,
                    "image_urls": ["https://...", "https://..."],
                    "whatsapp_dm_link": "https://wa.me/2348012345678?text=Hi%2C%20I%27m%20interested%20in%20Women%27s%20Ankara%20Gown"
                }
            ]
        }
    }
}
```

**Note:** Each view increments `views_count` by 1.

**WhatsApp DM Link:** When the user taps "Message on WhatsApp", open this URL. It's pre-formatted with a message about the specific item.

#### 6.4 Get My Portfolio (Auth)

```
GET /portfolios/my
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "portfolio": {
            "id": 1,
            "business_name": "Jane's Fashion House",
            "business_category": "Fashion",
            "business_description": "...",
            "whatsapp_number": "+2348012345678",
            "profile_image_url": "https://...",
            "cover_image_url": "https://...",
            "is_approved": true,
            "is_featured": false,
            "views_count": 246,
            "items": [
                {
                    "id": 10,
                    "title": "Women's Ankara Gown",
                    "description": "...",
                    "price": 25000,
                    "image_urls": ["https://..."],
                    "whatsapp_dm_link": "https://wa.me/...",
                    "is_active": true,
                    "created_at": "2026-07-01 14:30:00"
                }
            ]
        }
    }
}

Success 200 (no portfolio yet):
{
    "success": true,
    "message": "No portfolio found",
    "data": { "portfolio": null }
}
```

#### 6.5 Create or Update Portfolio (Auth)

```
POST /portfolios
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "business_name": "Jane's Fashion House",     // required
    "business_category": "Fashion",              // optional
    "business_description": "We sell quality...", // optional
    "whatsapp_number": "+2348012345678",         // optional
    "profile_image_url": "https://...",         // optional
    "cover_image_url": "https://..."            // optional
}

Success 200:
{
    "success": true,
    "message": "Portfolio saved successfully",
    "data": { "portfolio": Portfolio }
}
```

**Note:** This is an upsert — if the user already has a portfolio, it updates it. If not, it creates one.

#### 6.6 Add Portfolio Item (Auth)

```
POST /portfolios/items
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "title": "Women's Ankara Gown",              // required
    "description": "Beautiful gown...",          // optional
    "price": 25000,                              // optional, numeric
    "image_urls": ["https://..."],              // optional, array of URLs
    "whatsapp_dm_link": "https://wa.me/..."     // optional
}

Success 201:
{
    "success": true,
    "message": "Item added successfully",
    "data": { "item": PortfolioItem }
}

Error 400 (no portfolio yet):
{
    "success": false,
    "message": "Please create your portfolio first",
    "code": 400
}
```

#### 6.7 Update Portfolio Item (Auth)

```
PUT /portfolios/items/{id}
Authorization: Bearer <token>
Content-Type: application/json

Body (all fields optional):
{
    "title": "Updated Gown",
    "price": 27000,
    "is_active": true
}
```

#### 6.8 Delete Portfolio Item (Auth)

```
DELETE /portfolios/items/{id}
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "message": "Item deleted successfully",
    "data": []
}
```

---

## 7. Module 5 — Birthday Rewards

### Screens Needed
- Birthday Rewards Screen (eligibility + current reward status)
- Upcoming Birthdays Screen (list of members with birthdays this month)
- Admin: Birthday Management Screen
- Admin: Assign Gift Screen (select provider + item)

### Endpoints

#### 7.1 Check Eligibility (Auth)

```
GET /birthdays/eligibility
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "date_of_birth": "1995-12-25",
        "birthday_this_month": false,
        "total_subscription_months": 10,
        "is_eligible_for_reward": true,       // true if >= 8 months
        "is_shoutout_only": false,            // true if < 8 months
        "current_reward": {
            "id": 3,
            "status": "processing",           // "pending" | "processing" | "delivered" | "cancelled"
            "reward_type": "physical_delivery",
            "is_shoutout_only": false
        }                                     // null if no reward this month
    }
}
```

**Display logic:**
- If `birthday_this_month` is false → show "Your birthday is in {month}"
- If `birthday_this_month` is true AND `is_eligible_for_reward` → show "🎉 You are eligible!"
- If `is_shoutout_only` → show "You'll receive a birthday shoutout"
- If `current_reward.status` is "delivered" → show green checkmark

#### 7.2 Upcoming Birthdays (Public)

```
GET /birthdays/upcoming

Success 200:
{
    "success": true,
    "data": {
        "month": "July 2026",
        "birthdays": [
            {
                "user_id": 5,
                "name": "John Doe",
                "profile_photo_url": null,
                "birth_date": "2026-07-15",
                "age": 31,
                "days_until_birthday": 5,
                "rank": "vip",
                "total_subscription_months": 10,
                "business": {
                    "name": "Tech Solutions",
                    "category": "Technology"
                }
            }
        ]
    }
}
```

**Display:** Sort by `days_until_birthday` ascending. Show "Today!" if 0 days. Users with `business.name` can be tapped to view their portfolio.

#### 7.3 View My Reward (Auth)

```
GET /birthdays/my-reward
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "reward": {
            "id": 3,
            "reward_type": "physical_delivery",
            "status": "processing",
            "is_shoutout_only": false,
            "gift_provider": {
                "id": 8,
                "name": "Jane Smith"
            },
            "gift_item": {
                "id": 10,
                "title": "Women's Ankara Gown",
                "price": 25000
            },
            "created_at": "2026-07-01 10:00:00"
        }
    }
}

Success 200 (no reward):
{
    "success": true,
    "message": "No reward for this month",
    "data": { "reward": null }
}
```

---

## 8. Module 6 — Content Submission & Review

### Screens Needed
- Submit Content Screen
- My Submissions Screen (list with status)
- Submission Detail Screen
- Admin: Content Review Queue Screen
- Admin: Content Review Action (approve/reject)

### Endpoints

#### 8.1 Submit Content (Auth)

```
POST /content/submit
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "content_type": "advertisement",      // required: "advertisement" | "promotional_video" | "graphic" | "photo"
    "title": "Summer Collection 2026",     // required, max 200 chars
    "description": "Our new summer line...", // optional
    "media_url": "https://cloudinary.com/video.mp4", // required, valid URL
    "duration_seconds": 180               // optional, integer, max 300 (5 minutes)
}

Success 201:
{
    "success": true,
    "message": "Content submitted for review. You will be notified within 72 hours.",
    "data": {
        "submission": {
            "id": 25,
            "title": "Summer Collection 2026",
            "status": "pending_review",
            "submitted_at": "2026-07-10 10:30:00",
            "estimated_review_completion": "2026-07-13 10:30:00"
        }
    }
}

Error 422 (video too long):
{
    "success": false,
    "message": "The duration seconds must not be greater than 300.",
    "data": { "duration_seconds": ["..."], "code": 422 }
}
```

**Validation rules for mobile:**
- `duration_seconds` must be ≤ 300 (5 minutes) for videos
- `media_url` must be a valid URL (upload to Cloudinary or similar first, then send the URL)

#### 8.2 My Submissions (Auth)

```
GET /content/my-submissions?page=1&per_page=20
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "submissions": [
            {
                "id": 25,
                "title": "Summer Collection 2026",
                "content_type": "advertisement",
                "status": "pending_review",
                "submitted_at": "2026-07-10 10:30:00",
                "reviewed_at": null,
                "rejection_reason": null,
                "estimated_review_completion": "2026-07-13 10:30:00"
            }
        ],
        "pagination": { ... }
    }
}
```

#### 8.3 Submission Status (Auth)

```
GET /content/submission/{id}
Authorization: Bearer <token>

Success 200: Same shape as above + includes media_url
```

---

## 9. Module 7 — Advertisements

### Screens Needed
- Display ads on homepage/sidebar
- Create Ad Screen
- My Ads Screen
- Admin: Ad Management Screen

### Endpoints

#### 9.1 Get Active Ads (Public)

```
GET /ads?placement=homepage

Success 200:
{
    "success": true,
    "data": {
        "ads": [
            {
                "id": 1,
                "title": "Summer Sale!",
                "description": "Get 20% off...",
                "media_url": "https://...",
                "target_url": "https://wa.me/...",
                "placement": "homepage",
                "advertiser": "Jane Doe",
                "created_at": "2026-07-01 10:00:00"
            }
        ]
    }
}
```

#### 9.2 Create Ad (Auth)

```
POST /ads
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "title": "Summer Sale!",                           // required
    "description": "Get 20% off all items",            // optional
    "media_url": "https://...",                       // optional
    "target_url": "https://wa.me/...",                // optional
    "placement": "homepage",                          // required: "homepage" | "catalogue_page" | "sidebar"
    "end_date": "2026-07-17"                          // required, max 30 days from today
}
```

#### 9.3 My Ads (Auth)

```
GET /ads/my
Authorization: Bearer <token>
```

---

## 10. Module 8 — Wallet & Earnings

### Screens Needed
- Wallet Screen (balance + transactions)
- Withdraw Screen

### Endpoints

#### 10.1 Get Balance (Auth)

```
GET /wallet/balance
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "balance": 12500.00,
        "currency": "NGN"
    }
}
```

#### 10.2 Get Transactions (Auth)

```
GET /wallet/transactions?type=referral_bonus&status=completed&page=1&per_page=20
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "transactions": [
            {
                "id": 1,
                "type": "referral_bonus",
                "amount": 500.00,
                "balance_before": 12000.00,
                "balance_after": 12500.00,
                "reference": "REF-ABC123",
                "description": "Referral bonus for John Doe",
                "status": "completed",
                "created_at": "2026-07-08 14:30:00"
            },
            {
                "id": 2,
                "type": "withdrawal",
                "amount": -5000.00,
                "balance_before": 17500.00,
                "balance_after": 12500.00,
                "reference": "WDL-XYZ789",
                "description": "Bank transfer withdrawal",
                "status": "completed",
                "created_at": "2026-07-05 09:15:00"
            }
        ],
        "pagination": { ... }
    }
}
```

**Display:** Positive amounts = green/credit. Negative amounts = red/debit.

#### 10.3 Withdraw (Auth)

```
POST /wallet/withdraw
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "amount": 5000,               // required, minimum 100
    "description": "Withdrawal to GTBank"  // optional
}

Success 200:
{
    "success": true,
    "message": "Withdrawal request submitted successfully",
    "data": {
        "reference": "WDL-ABC123",
        "amount": 5000,
        "new_balance": 7500
    }
}

Error 400 (insufficient):
{
    "success": false,
    "message": "Insufficient balance. Available: ₦1,000.00",
    "code": 400
}
```

---

## 11. Module 9 — Member Rankings

### Screens Needed
- My Rank Screen (current rank + progress to next)
- Leaderboard Screen

### Endpoints

#### 11.1 My Rank (Auth)

```
GET /rankings/my
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "ranking": {
            "current_rank": "vip",
            "total_months_subscribed": 10,
            "consecutive_months": 10,
            "rank_achieved_at": "2026-07-01T00:00:00.000000Z",
            "next_rank_info": {
                "next_rank": "vvip",
                "months_remaining": 2,
                "progress_percentage": 83
            }
        }
    }
}
```

**Display logic:**
- Show rank badge with color: Regular (bronze/blue), VIP (silver/purple), VVIP (gold/red)
- Show progress bar: `progress_percentage`% to next rank
- Show `months_remaining` to next rank

#### 11.2 Leaderboard (Public)

```
GET /rankings/leaderboard

Success 200:
{
    "success": true,
    "data": {
        "leaderboard": [
            {
                "user_id": 1,
                "name": "John Doe",
                "email": "john@example.com",
                "profile_photo_url": null,
                "rank": "vvip",
                "total_months": 14
            },
            {
                "user_id": 5,
                "name": "Jane Doe",
                "email": "jane@example.com",
                "profile_photo_url": "https://...",
                "rank": "vip",
                "total_months": 10
            }
        ]
    }
}
```

**Display:** Rank order: VVIP first, then VIP, then Regular. Within each tier, sorted by months descending.

---

## 12. Module 10 — Referral System

### Screens Needed
- Refer & Earn Screen
- Referred Users List Screen

### Endpoints

#### 12.1 Get My Referral Link (Auth)

```
GET /referrals/my-link
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": [
        {
            "code": "JAN1A3F9K2B",
            "link": "https://yourdomain.com/register?ref=JAN1A3F9K2B",
            "program": "Sign-up Bonus",
            "created_at": "2026-07-05 06:43:39"
        }
    ]
}
```

**Share functionality:** Use the `link` field for social sharing and the `code` field for display.

#### 12.2 Get Referral Stats (Auth)

```
GET /referrals/stats
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "total_referrals": 12,
        "active_referrals": 12,
        "total_earnings": 6000,
        "pending_rewards": 0,
        "available_balance": 6000
    }
}
```

#### 12.3 Get Referred Users (Auth)

```
GET /referrals/referred-users?page=1&per_page=15
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "referred_users": [
            {
                "id": 42,
                "first_name": "Jane",
                "last_name": "Doe",
                "email": "jane@example.com",
                "referred_at": "2026-07-04 12:30:00"
            }
        ],
        "pagination": { ... }
    }
}
```

---

## 13. Module 11 — Notifications

### Screens Needed
- Notifications List Screen
- Notification Preferences Screen

### Endpoints

#### 13.1 Get Notifications (Auth)

```
GET /notifications?page=1&per_page=20&type=system&unread_only=true
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": [
        {
            "id": 1,
            "type": "transaction",
            "title": "Referral Bonus",
            "body": "You earned ₦500 for referring John Doe",
            "data": null,
            "priority": "normal",
            "read_at": null,
            "created_at": "2026-07-08T14:30:00.000000Z",
            "time_ago": "2 days ago"
        }
    ],
    "pagination": { ... }
}
```

#### 13.2 Mark as Read (Auth)

```
PUT /notifications/{id}/read
Authorization: Bearer <token>
```

#### 13.3 Mark All Read (Auth)

```
PUT /notifications/mark-all-read
Authorization: Bearer <token>
```

#### 13.4 Get Notification Stats (Auth)

```
GET /notifications/stats
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "total": 25,
        "unread": 3,
        "by_type": { "transaction": 15, "system": 5, "promotion": 5 },
        "by_priority": { "normal": 20, "high": 5 }
    }
}
```

#### 13.5 Register Push Token (Auth)

```
POST /notifications/register-token
Authorization: Bearer <token>
Content-Type: application/json

Body:
{
    "expo_push_token": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
    "device_info": { "platform": "ios", "version": "18.0" }
}
```

**Important for mobile:** Register the Expo push token on login/app launch. This enables push notifications.

---

## 14. Module 12 — Admin Dashboard

### Screens Needed
- Admin Dashboard Home (stats overview)
- User Management Screen
- Subscription Management Screen
- Content Review Queue Screen
- Birthday Management Screen
- Portfolio Approval Screen
- Ad Management Screen
- Role & Permission Management Screen

### Endpoints

#### 14.1 Dashboard Stats (Admin)

```
GET /admin/stats
Authorization: Bearer <token>

Success 200: Large response with all analytics. Key sections:
{
    "stats": {
        "user_overview": { "total_users": 245, "active_users": 200, ... },
        "registration_trend": { "daily_30_days": [...], "monthly_6_months": [...], "today": 3, ... },
        "role_distribution": { "admin": 2, "user": 150, "titan_member": 62, ... },
        "subscription_analytics": {
            "total_active_subscriptions": 155,
            "total_revenue": 425000,
            "revenue_this_month": 425000,
            "subscriptions_by_plan": [
                { "plan": "Basic", "slug": "basic", "price": 2000, "count": 45, "revenue": 90000 },
                { "plan": "Standard", "slug": "standard", "price": 3500, "count": 78, "revenue": 273000 },
                { "plan": "Premium", "slug": "premium", "price": 5000, "count": 32, "revenue": 160000 }
            ]
        },
        "member_rankings": {
            "total_titan_members": 62,
            "rank_distribution": { "regular": 25, "vip": 20, "vvip": 17 }
        },
        "birthday_analytics": {
            "eligible_members_this_month": 8,
            "rewards_this_month": 5,
            "pending_rewards": 3,
            "delivered_rewards": 2
        },
        "content_review": {
            "pending_reviews": 12,
            "approved": 45,
            "rejected": 8
        },
        "portfolio_stats": {
            "total_portfolios": 62,
            "approved": 58,
            "pending_approval": 4
        }
    }
}
```

#### 14.2 User Management (Admin)

```
GET /admin/users?page=1&per_page=20&search=john&role=user&status=active&is_verified=verified&sort_by=created_at&sort_order=desc
Authorization: Bearer <token>

Success 200:
{
    "success": true,
    "data": {
        "users": [
            {
                "id": 1,
                "first_name": "John",
                "last_name": "Doe",
                "email": "john@example.com",
                "phone_number": "08123456789",
                "is_active": true,
                "is_verified": true,
                "roles": ["titan_member"],
                "permissions": [...],
                "profile_photo_url": null,
                "is_profile_complete": true,
                "created_at": "2026-07-01 10:30:00",
                "deleted_at": null
            }
        ],
        "pagination": { ... }
    }
}
```

```
GET /admin/users/{id}
Authorization: Bearer <token>
— Returns full user detail with referral info.

PUT /admin/users/{id}
Authorization: Bearer <token>
Body: { "first_name": "...", "roles": ["titan_member"] }

DELETE /admin/users/{id}
— Soft delete (cannot delete admin users)

POST /admin/users/{id}/restore
POST /admin/users/{id}/toggle-active
GET /admin/users/{id}/login-history
```

#### 14.3 Admin — Subscriptions

```
GET /admin/subscriptions?plan_id=1&payment_status=paid&is_active=true
POST /admin/subscriptions/plans — Create plan
PUT /admin/subscriptions/plans/{id} — Update plan
DELETE /admin/subscriptions/plans/{id} — Deactivate plan
```

#### 14.4 Admin — Content Review

```
GET /admin/content/pending
— Queue ordered by submitted_at ASC (oldest first)

POST /admin/content/{id}/approve
— No body needed, just the ID

POST /admin/content/{id}/reject
Body: { "reason": "Inappropriate content" }
```

#### 14.5 Admin — Birthday Management

```
GET /admin/birthdays?month=2026-07&status=pending
GET /admin/birthdays/eligible
— Members eligible this month (birthday month + >= 8 months)

POST /admin/birthdays/assign-gift
Body: {
    "user_id": 5,               // celebrant
    "gift_provider_user_id": 12, // member providing the gift
    "gift_portfolio_item_id": 8, // the specific item
    "reward_type": "physical_delivery"  // optional
}

POST /admin/birthdays/{id}/mark-delivered
```

#### 14.6 Admin — Portfolio Management

```
GET /admin/portfolios?status=pending
PUT /admin/portfolios/{id}
Body: { "is_approved": true, "is_featured": true }
```

#### 14.7 Admin — Ad Management

```
GET /admin/ads?status=active
PUT /admin/ads/{id}
Body: { "status": "cancelled" }
```

---

## 15. Deep Linking for Referrals

The app must handle deep links so that when a user taps a referral link `https://yourdomain.com/register?ref=CODE123`, the app opens and extracts the `ref` parameter.

**Implementation:**
1. Register a deep link scheme (e.g., `businessbirthday://` or use universal links)
2. On app cold start or when app comes to foreground via deep link, parse the URL for `?ref=CODE`
3. Store the referral code temporarily
4. On the Register screen, auto-fill the referral code field and show a banner "You were referred by a friend! 🎉"

**Deep link URL format:**
```
https://yourdomain.com/register?ref=JAN1A3F9K2B
```

---

## 16. Error Response Formats

All API errors follow a consistent pattern. Handle these in the API client/interceptor layer.

```json
// Validation Error (422) — Show field-specific errors
{
    "success": false,
    "message": "The email has already been taken.",
    "data": { "email": ["The email has already been taken."] },
    "code": 422
}

// Authentication Error (401) — Clear token, redirect to login
{
    "success": false,
    "message": "Unauthenticated",
    "code": 401
}

// Forbidden (403) — Show message to user
{
    "success": false,
    "message": "No active subscription. Please subscribe to a plan.",
    "code": 403
}

// Not Found (404) — Show "Not found" screen
{
    "success": false,
    "message": "Portfolio not found",
    "code": 404
}

// Rate Limited (429) — Show "Please wait" message
{
    "success": false,
    "message": "Please wait before requesting a new OTP",
    "code": 429
}

// Server Error (500) — Show generic error message
{
    "success": false,
    "message": "Failed to fetch plans",
    "code": 500
}
```

**Generic success response for all endpoints:**
```json
{
    "success": true,
    "message": "Action completed successfully",
    "data": { ... }
}
```

---

## Appendix: Screen-to-Endpoint Mapping

| Mobile Screen | API Endpoints to Call |
|---------------|----------------------|
| Splash Screen | GET /auth/verify (check token) |
| Login Screen | POST /auth/login |
| Register Screen | POST /auth/register (include ref from deep link) |
| Email Verification | POST /auth/verify-email-with-otp, POST /auth/resend-email-verification-otp |
| Forgot Password | POST /auth/forgot-password, POST /auth/verify-password-reset-otp, POST /auth/reset-password |
| Home Dashboard | GET /subscriptions/my-subscription, GET /rankings/my, GET /birthdays/upcoming |
| Subscription Plans | GET /subscriptions/plans |
| Subscribe | POST /subscriptions/subscribe |
| My Subscription | GET /subscriptions/my-subscription |
| Subscription History | GET /subscriptions/history |
| Browse Catalogues | GET /portfolios, GET /portfolios/categories |
| Single Portfolio | GET /portfolios/{id} |
| My Portfolio | GET /portfolios/my, POST /portfolios |
| Add/Edit Item | POST /portfolios/items, PUT /portfolios/items/{id}, DELETE /portfolios/items/{id} |
| Birthday Rewards | GET /birthdays/eligibility, GET /birthdays/my-reward |
| Upcoming Birthdays | GET /birthdays/upcoming |
| Refer & Earn | GET /referrals/my-link, GET /referrals/stats |
| Referred Users | GET /referrals/referred-users |
| Wallet | GET /wallet/balance, GET /wallet/transactions |
| Withdraw | POST /wallet/withdraw |
| My Rank | GET /rankings/my |
| Leaderboard | GET /rankings/leaderboard |
| Content Submission | POST /content/submit |
| My Submissions | GET /content/my-submissions |
| Notifications | GET /notifications, PUT /notifications/{id}/read |
| Notification Prefs | GET /notifications/preferences, PUT /notifications/preferences |
| Profile | GET /auth/profile, PUT /auth/profile |
| Change Password | POST /auth/change-password |
| **Admin Dashboard** | GET /admin/stats |
| Admin Users | GET /admin/users, GET /admin/users/{id}, PUT /admin/users/{id}, DELETE /admin/users/{id} |
| Admin Subscriptions | GET /admin/subscriptions, POST /admin/subscriptions/plans |
| Admin Content Review | GET /admin/content/pending, POST /admin/content/{id}/approve, POST /admin/content/{id}/reject |
| Admin Birthdays | GET /admin/birthdays, GET /admin/birthdays/eligible, POST /admin/birthdays/assign-gift, POST /admin/birthdays/{id}/mark-delivered |
| Admin Portfolios | GET /admin/portfolios, PUT /admin/portfolios/{id} |
| Admin Ads | GET /admin/ads, PUT /admin/ads/{id} |
| Admin Roles | GET /role/roles, POST /role/roles, etc. |
| Admin Permissions | GET /role/permissions, POST /role/permissions, etc. |
