{"openapi":"3.0.3","info":{"title":"Anas LMS API","version":"1.0.0","description":"\n# Anas English Learning LMS Platform API\n\nREST API for an English Learning LMS platform serving both online adult learners and offline children students in Bangladesh.\n\n## Features\n- **Authentication** - JWT-based auth with refresh tokens\n- **Course Management** - Courses, modules, and lessons\n- **Video Streaming** - BunnyCDN integration\n- **Payments** - bKash, Nagad, SSLCommerz\n- **Progress Tracking** - Watch time and completion\n- **Certificates** - Auto-generated on completion\n- **Admin Dashboard** - User, order, and revenue management\n\n## Authentication\nMost endpoints require authentication via Bearer token:\n```\nAuthorization: Bearer <access_token>\n```\n\n## Rate Limiting\n- **General API:** 100 requests per 15 minutes\n- **Auth endpoints:** 5 requests per 15 minutes\n- **Payment endpoints:** 10 requests per 15 minutes\n      ","contact":{"name":"Anas LMS Support","email":"support@anaslms.com"},"license":{"name":"Proprietary"}},"servers":[{"url":"http://localhost:8000/api/v1","description":"Development server"},{"url":"https://api.anaslms.com/api/v1","description":"Production server"}],"tags":[{"name":"Auth","description":"Authentication and authorization"},{"name":"Users","description":"User management"},{"name":"Courses","description":"Course management"},{"name":"Modules","description":"Course module management"},{"name":"Lessons","description":"Lesson management"},{"name":"Enrollment","description":"Student enrollment"},{"name":"Progress","description":"Progress tracking"},{"name":"Payments","description":"Payment processing"},{"name":"Orders","description":"Order management"},{"name":"Videos","description":"Video upload and streaming"},{"name":"Admin","description":"Admin dashboard operations"},{"name":"Books","description":"Book recommendations"},{"name":"Offline Registration","description":"Offline student registration"},{"name":"Public","description":"Public website endpoints"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Enter your JWT access token"}},"schemas":{"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object"},"message":{"type":"string"}}},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","properties":{"code":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string","example":"Invalid request data"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}}}},"PaginatedResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{}},"meta":{"type":"object","properties":{"page":{"type":"integer","example":1},"limit":{"type":"integer","example":20},"total":{"type":"integer","example":100},"totalPages":{"type":"integer","example":5}}}}},"RegisterRequest":{"type":"object","required":["email","password","name"],"properties":{"email":{"type":"string","format":"email","example":"student@example.com"},"phone":{"type":"string","example":"+8801712345678"},"password":{"type":"string","minLength":8,"example":"SecurePass123!"},"name":{"type":"string","example":"John Doe"}}},"LoginRequest":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email","example":"student@example.com"},"password":{"type":"string","example":"SecurePass123!"}}},"AuthResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}},"RefreshTokenRequest":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}},"ChangePasswordRequest":{"type":"object","required":["currentPassword","newPassword"],"properties":{"currentPassword":{"type":"string"},"newPassword":{"type":"string","minLength":8}}},"ForgotPasswordRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"ResetPasswordRequest":{"type":"object","required":["token","password"],"properties":{"token":{"type":"string"},"password":{"type":"string","minLength":8}}},"User":{"type":"object","properties":{"id":{"type":"string","example":"clx1234567890"},"email":{"type":"string","format":"email"},"phone":{"type":"string","nullable":true},"name":{"type":"string"},"photoUrl":{"type":"string","nullable":true},"role":{"type":"string","enum":["STUDENT","TEACHER","ADMIN"]},"status":{"type":"string","enum":["ACTIVE","BANNED","DEACTIVATED"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"UpdateProfileRequest":{"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"}}},"Course":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string","example":"Complete English Speaking Course"},"description":{"type":"string"},"type":{"type":"string","enum":["ONLINE","OFFLINE"]},"price":{"type":"number","example":2999},"thumbnailUrl":{"type":"string","nullable":true},"published":{"type":"boolean"},"modules":{"type":"array","items":{"$ref":"#/components/schemas/Module"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateCourseRequest":{"type":"object","required":["title","description","type","price"],"properties":{"title":{"type":"string","minLength":3,"maxLength":200},"description":{"type":"string","minLength":10},"type":{"type":"string","enum":["ONLINE","OFFLINE"]},"price":{"type":"number","minimum":0},"thumbnailUrl":{"type":"string"}}},"UpdateCourseRequest":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":200},"description":{"type":"string","minLength":10},"type":{"type":"string","enum":["ONLINE","OFFLINE"]},"price":{"type":"number","minimum":0},"thumbnailUrl":{"type":"string"},"published":{"type":"boolean"}}},"Module":{"type":"object","properties":{"id":{"type":"string"},"courseId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"order":{"type":"integer"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/Lesson"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateModuleRequest":{"type":"object","required":["title","order"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"order":{"type":"integer","minimum":1}}},"Lesson":{"type":"object","properties":{"id":{"type":"string"},"moduleId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"videoId":{"type":"string","nullable":true},"videoUrl":{"type":"string","nullable":true},"duration":{"type":"integer","nullable":true,"description":"Duration in seconds"},"textContent":{"type":"string","nullable":true},"order":{"type":"integer"},"materials":{"type":"array","items":{"$ref":"#/components/schemas/LessonMaterial"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateLessonRequest":{"type":"object","required":["title","order"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"textContent":{"type":"string"},"order":{"type":"integer","minimum":1}}},"LessonMaterial":{"type":"object","properties":{"id":{"type":"string"},"lessonId":{"type":"string"},"title":{"type":"string"},"fileUrl":{"type":"string"},"fileType":{"type":"string"},"fileSize":{"type":"integer","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Enrollment":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"courseId":{"type":"string"},"progressPercentage":{"type":"number","example":45.5},"completedAt":{"type":"string","format":"date-time","nullable":true},"certificateUrl":{"type":"string","nullable":true},"enrolledAt":{"type":"string","format":"date-time"},"course":{"$ref":"#/components/schemas/Course"}}},"LessonProgress":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"lessonId":{"type":"string"},"watchedSeconds":{"type":"integer"},"totalSeconds":{"type":"integer"},"completed":{"type":"boolean"},"lastWatchedAt":{"type":"string","format":"date-time"}}},"UpdateProgressRequest":{"type":"object","required":["watchedSeconds","totalSeconds"],"properties":{"watchedSeconds":{"type":"integer","minimum":0},"totalSeconds":{"type":"integer","minimum":0},"completed":{"type":"boolean"}}},"CreatePaymentRequest":{"type":"object","required":["courseId","paymentMethod"],"properties":{"courseId":{"type":"string"},"paymentMethod":{"type":"string","enum":["BKASH","NAGAD","SSLCOMMERZ"]},"couponCode":{"type":"string"}}},"PaymentResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"orderId":{"type":"string"},"paymentUrl":{"type":"string"},"amount":{"type":"number"}}}}},"VerifyCouponRequest":{"type":"object","required":["code","courseId"],"properties":{"code":{"type":"string"},"courseId":{"type":"string"}}},"Order":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"courseId":{"type":"string"},"amount":{"type":"number"},"originalAmount":{"type":"number"},"paymentMethod":{"type":"string","enum":["BKASH","NAGAD","SSLCOMMERZ","OFFLINE"]},"paymentStatus":{"type":"string","enum":["PENDING","COMPLETED","FAILED","REFUNDED"]},"transactionId":{"type":"string","nullable":true},"couponCode":{"type":"string","nullable":true},"discountAmount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"user":{"$ref":"#/components/schemas/User"},"course":{"$ref":"#/components/schemas/Course"}}},"VideoUploadUrlResponse":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"uploadUrl":{"type":"string"},"videoId":{"type":"string"}}}}},"Coupon":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"type":{"type":"string","enum":["PERCENTAGE","FIXED"]},"value":{"type":"number"},"validFrom":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"usageLimit":{"type":"integer","nullable":true},"usedCount":{"type":"integer"},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"CreateCouponRequest":{"type":"object","required":["code","type","value","validFrom","validUntil"],"properties":{"code":{"type":"string"},"type":{"type":"string","enum":["PERCENTAGE","FIXED"]},"value":{"type":"number","minimum":0},"validFrom":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"usageLimit":{"type":"integer","nullable":true},"courseIds":{"type":"array","items":{"type":"string"}}}},"Book":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"author":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"coverUrl":{"type":"string"},"purchaseLink":{"type":"string"},"order":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"}}},"CreateBookRequest":{"type":"object","required":["title","author","description","price","coverUrl","purchaseLink","order"],"properties":{"title":{"type":"string"},"author":{"type":"string"},"description":{"type":"string"},"price":{"type":"number","minimum":0},"coverUrl":{"type":"string"},"purchaseLink":{"type":"string"},"order":{"type":"integer","minimum":1}}},"OfflineRegistration":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"courseId":{"type":"string"},"paymentType":{"type":"string","enum":["ONLINE","OFFLINE"]},"paymentStatus":{"type":"string","enum":["PENDING_PAYMENT","PAID","ENROLLED"]},"amount":{"type":"number"},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"course":{"$ref":"#/components/schemas/Course"}}},"CreateOfflineRegistrationRequest":{"type":"object","required":["name","email","phone","courseId","paymentType"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"courseId":{"type":"string"},"paymentType":{"type":"string","enum":["ONLINE","OFFLINE"]},"notes":{"type":"string"}}},"Lead":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string","nullable":true},"signupDate":{"type":"string","format":"date-time"},"lastActivity":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["NEW","CONTACTED","CONVERTED","NOT_INTERESTED"]},"notes":{"type":"string","nullable":true},"cartAbandoned":{"type":"boolean"},"incompleteSignup":{"type":"boolean"}}},"Testimonial":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"role":{"type":"string","nullable":true},"content":{"type":"string"},"rating":{"type":"integer","minimum":1,"maximum":5},"photoUrl":{"type":"string","nullable":true}}},"FAQ":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"category":{"type":"string","nullable":true}}},"ContactRequest":{"type":"object","required":["name","email","subject","message"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"subject":{"type":"string"},"message":{"type":"string"}}},"RevenueDashboard":{"type":"object","properties":{"totalRevenue":{"type":"number"},"monthlyRevenue":{"type":"number"},"weeklyRevenue":{"type":"number"},"todayRevenue":{"type":"number"},"totalOrders":{"type":"integer"},"completedOrders":{"type":"integer"},"pendingOrders":{"type":"integer"},"totalStudents":{"type":"integer"},"activeStudents":{"type":"integer"},"revenueByMonth":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string"},"revenue":{"type":"number"}}}},"revenueByPaymentMethod":{"type":"array","items":{"type":"object","properties":{"method":{"type":"string"},"total":{"type":"number"}}}}}},"LeaderboardEntry":{"type":"object","properties":{"rank":{"type":"integer"},"userId":{"type":"string"},"name":{"type":"string"},"photoUrl":{"type":"string","nullable":true},"coursesCompleted":{"type":"integer"},"totalWatchTime":{"type":"integer","description":"Total watch time in seconds"},"score":{"type":"number"}}}},"parameters":{"PageParam":{"name":"page","in":"query","schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},"LimitParam":{"name":"limit","in":"query","schema":{"type":"integer","default":20,"minimum":1,"maximum":100},"description":"Items per page"},"IdParam":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Resource ID"},"CourseIdParam":{"name":"courseId","in":"path","required":true,"schema":{"type":"string"},"description":"Course ID"},"ModuleIdParam":{"name":"moduleId","in":"path","required":true,"schema":{"type":"string"},"description":"Module ID"},"LessonIdParam":{"name":"lessonId","in":"path","required":true,"schema":{"type":"string"},"description":"Lesson ID"}},"responses":{"UnauthorizedError":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"AUTHENTICATION_ERROR","message":"Authentication required"}}}}},"ForbiddenError":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"AUTHORIZATION_ERROR","message":"You do not have permission to access this resource"}}}}},"NotFoundError":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"NOT_FOUND","message":"Resource not found"}}}}},"ValidationError":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Invalid request data","details":[{"field":"email","message":"Invalid email format"}]}}}}},"RateLimitError":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"RATE_LIMIT_ERROR","message":"Too many requests, please try again later"}}}}}}},"security":[{"BearerAuth":[]}],"paths":{"/admin/users":{"get":{"tags":["Admin"],"summary":"List all users","description":"Get a paginated list of all users with filters (admin only)","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"role","in":"query","schema":{"type":"string","enum":["STUDENT","TEACHER","ADMIN"]},"description":"Filter by role"},{"name":"status","in":"query","schema":{"type":"string","enum":["ACTIVE","BANNED","DEACTIVATED"]},"description":"Filter by status"},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search by name, email, or phone"}],"responses":{"200":{"description":"List of users","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/users/{id}":{"get":{"tags":["Admin"],"summary":"Get user details","description":"Get detailed information about a specific user (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"allOf":[{"$ref":"#/components/schemas/User"},{"type":"object","properties":{"enrollments":{"type":"array","items":{"$ref":"#/components/schemas/Enrollment"}},"orders":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"leadTracking":{"$ref":"#/components/schemas/Lead"}}}]}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/users/{id}/status":{"patch":{"tags":["Admin"],"summary":"Update user status","description":"Ban, unban, or deactivate a user (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["ACTIVE","BANNED","DEACTIVATED"]},"reason":{"type":"string","description":"Reason for status change (for audit)"}}}}}},"responses":{"200":{"description":"User status updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/User"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/users/{id}/enroll":{"post":{"tags":["Admin"],"summary":"Manually enroll user","description":"Manually enroll a user in a course (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["courseId"],"properties":{"courseId":{"type":"string"},"notes":{"type":"string","description":"Reason for manual enrollment"}}}}}},"responses":{"201":{"description":"User enrolled successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Enrollment"}}}}}},"400":{"description":"User already enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/orders":{"get":{"tags":["Admin"],"summary":"List all orders","description":"Get a paginated list of all orders with filters (admin only)","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","COMPLETED","FAILED","REFUNDED"]},"description":"Filter by payment status"},{"name":"paymentMethod","in":"query","schema":{"type":"string","enum":["BKASH","NAGAD","SSLCOMMERZ","OFFLINE"]},"description":"Filter by payment method"},{"name":"courseId","in":"query","schema":{"type":"string"},"description":"Filter by course"},{"name":"startDate","in":"query","schema":{"type":"string","format":"date"},"description":"Filter orders from this date"},{"name":"endDate","in":"query","schema":{"type":"string","format":"date"},"description":"Filter orders until this date"}],"responses":{"200":{"description":"List of orders","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/orders/export":{"get":{"tags":["Admin"],"summary":"Export orders to CSV","description":"Export filtered orders to a CSV file (admin only)","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","COMPLETED","FAILED","REFUNDED"]}},{"name":"startDate","in":"query","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"CSV file","content":{"text/csv":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Disposition":{"schema":{"type":"string","example":"attachment; filename=\"orders-2024-01.csv\""}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/revenue/dashboard":{"get":{"tags":["Admin"],"summary":"Get revenue dashboard","description":"Get revenue statistics and trends (admin only)","parameters":[{"name":"period","in":"query","schema":{"type":"string","enum":["week","month","quarter","year"],"default":"month"},"description":"Time period for trends"}],"responses":{"200":{"description":"Revenue dashboard data","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/RevenueDashboard"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/students/active":{"get":{"tags":["Admin"],"summary":"List active students","description":"Get a list of currently enrolled students with activity metrics (admin only)","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"courseId","in":"query","schema":{"type":"string"},"description":"Filter by course"}],"responses":{"200":{"description":"List of active students","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"enrolledCourses":{"type":"integer"},"completedCourses":{"type":"integer"},"lastActivity":{"type":"string","format":"date-time"},"totalWatchTime":{"type":"integer"}}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/leads":{"get":{"tags":["Admin"],"summary":"List leads","description":"Get a list of potential customers for follow-up (admin only)","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"status","in":"query","schema":{"type":"string","enum":["NEW","CONTACTED","CONVERTED","NOT_INTERESTED"]},"description":"Filter by lead status"},{"name":"cartAbandoned","in":"query","schema":{"type":"boolean"},"description":"Filter by cart abandonment"}],"responses":{"200":{"description":"List of leads","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/leads/{id}":{"patch":{"tags":["Admin"],"summary":"Update lead status","description":"Update lead status and add notes (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["NEW","CONTACTED","CONVERTED","NOT_INTERESTED"]},"notes":{"type":"string"}}}}}},"responses":{"200":{"description":"Lead updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Lead"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/coupons":{"get":{"tags":["Admin"],"summary":"List all coupons","description":"Get a list of all coupons (admin only)","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"active","in":"query","schema":{"type":"boolean"},"description":"Filter by active status"}],"responses":{"200":{"description":"List of coupons","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Coupon"}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}},"post":{"tags":["Admin"],"summary":"Create a coupon","description":"Create a new discount coupon (admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCouponRequest"}}}},"responses":{"201":{"description":"Coupon created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Coupon"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"409":{"description":"Coupon code already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/admin/coupons/{id}":{"patch":{"tags":["Admin"],"summary":"Update a coupon","description":"Update coupon details (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"number"},"validFrom":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time"},"usageLimit":{"type":"integer","nullable":true},"active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Coupon updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Coupon"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}},"delete":{"tags":["Admin"],"summary":"Delete a coupon","description":"Delete a coupon (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"204":{"description":"Coupon deleted"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/books":{"get":{"tags":["Admin"],"summary":"List all books","description":"Get a list of all book recommendations (admin only)","responses":{"200":{"description":"List of books","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Book"}}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}},"post":{"tags":["Admin"],"summary":"Create a book","description":"Add a new book recommendation (admin only)","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["title","author","description","price","purchaseLink","order","cover"],"properties":{"title":{"type":"string"},"author":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"purchaseLink":{"type":"string"},"order":{"type":"integer"},"cover":{"type":"string","format":"binary","description":"Book cover image"}}}}}},"responses":{"201":{"description":"Book created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Book"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/books/{id}":{"patch":{"tags":["Admin"],"summary":"Update a book","description":"Update book details (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"author":{"type":"string"},"description":{"type":"string"},"price":{"type":"number"},"purchaseLink":{"type":"string"},"order":{"type":"integer"}}}}}},"responses":{"200":{"description":"Book updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Book"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}},"delete":{"tags":["Admin"],"summary":"Delete a book","description":"Remove a book recommendation (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"204":{"description":"Book deleted"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/books/reorder":{"patch":{"tags":["Admin"],"summary":"Reorder books","description":"Update the display order of books (admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["bookOrders"],"properties":{"bookOrders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"order":{"type":"integer"}}}}}}}}},"responses":{"200":{"description":"Books reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Books reordered successfully"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/offline-registrations":{"get":{"tags":["Admin"],"summary":"List offline registrations","description":"Get a list of offline student registrations (admin only)","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING_PAYMENT","PAID","ENROLLED"]},"description":"Filter by status"}],"responses":{"200":{"description":"List of registrations","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OfflineRegistration"}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/admin/offline-registrations/{id}/mark-paid":{"patch":{"tags":["Admin"],"summary":"Mark registration as paid","description":"Mark an offline registration as paid (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"string","description":"Payment verification notes"}}}}}},"responses":{"200":{"description":"Registration marked as paid","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/OfflineRegistration"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/admin/offline-registrations/{id}/enroll":{"patch":{"tags":["Admin"],"summary":"Enroll offline student","description":"Create user account and enroll the student (admin only).\n\nThis will:\n1. Create a new user account with generated password\n2. Create enrollment for the course\n3. Send welcome email with login credentials\n","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"200":{"description":"Student enrolled successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"registration":{"$ref":"#/components/schemas/OfflineRegistration"},"user":{"$ref":"#/components/schemas/User"},"enrollment":{"$ref":"#/components/schemas/Enrollment"}}}}}}}},"400":{"description":"Registration not paid yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/auth/register":{"post":{"tags":["Auth"],"summary":"Register a new user","description":"Create a new student account. Teachers and admins are created by admins only.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}}},"responses":{"201":{"description":"User registered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"409":{"description":"Email or phone already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"CONFLICT","message":"A user with this email already exists"}}}}},"429":{"$ref":"#/components/responses/RateLimitError"}}}},"/auth/login":{"post":{"tags":["Auth"],"summary":"Login with email and password","description":"Authenticate user and receive access and refresh tokens","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"Login successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"AUTHENTICATION_ERROR","message":"Invalid email or password"}}}}},"423":{"description":"Account locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"AUTHENTICATION_ERROR","message":"Account locked due to too many failed attempts. Try again in 15 minutes."}}}}},"429":{"$ref":"#/components/responses/RateLimitError"}}}},"/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh access token","description":"Get a new access token using a valid refresh token","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}}},"responses":{"200":{"description":"Token refreshed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}}}}},"401":{"description":"Invalid or expired refresh token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/auth/logout":{"post":{"tags":["Auth"],"summary":"Logout user","description":"Invalidate the current refresh token","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}}},"responses":{"200":{"description":"Logged out successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Logged out successfully"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Get current user profile","description":"Retrieve the authenticated user's profile information","responses":{"200":{"description":"User profile retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/User"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}},"patch":{"tags":["Auth"],"summary":"Update user profile","description":"Update the authenticated user's name or phone","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"photo":{"type":"string","format":"binary","description":"Profile photo (JPEG, PNG, WebP, max 5MB)"}}}}}},"responses":{"200":{"description":"Profile updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/User"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/auth/change-password":{"patch":{"tags":["Auth"],"summary":"Change password","description":"Change the authenticated user's password","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}}},"responses":{"200":{"description":"Password changed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Password changed successfully"}}}}}},"400":{"description":"Current password incorrect or new password too weak","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/auth/forgot-password":{"post":{"tags":["Auth"],"summary":"Request password reset","description":"Send a password reset email to the user","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}}},"responses":{"200":{"description":"Password reset email sent (always returns success for security)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"If an account exists with this email, a password reset link has been sent"}}}}}},"429":{"$ref":"#/components/responses/RateLimitError"}}}},"/auth/reset-password":{"post":{"tags":["Auth"],"summary":"Reset password with token","description":"Reset password using the token received via email","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}}},"responses":{"200":{"description":"Password reset successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Password reset successfully"}}}}}},"400":{"description":"Invalid or expired token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/courses":{"get":{"tags":["Courses"],"summary":"List all published courses","description":"Get a paginated list of all published courses. Supports filtering by type.","security":[],"parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"type","in":"query","schema":{"type":"string","enum":["ONLINE","OFFLINE"]},"description":"Filter by course type"},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search by title or description"}],"responses":{"200":{"description":"List of courses","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Course"}}}}]}}}}}},"post":{"tags":["Courses"],"summary":"Create a new course","description":"Create a new course (admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCourseRequest"}},"multipart/form-data":{"schema":{"type":"object","required":["title","description","type","price"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["ONLINE","OFFLINE"]},"price":{"type":"number"},"thumbnail":{"type":"string","format":"binary","description":"Course thumbnail image"}}}}}},"responses":{"201":{"description":"Course created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Course"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/courses/{id}":{"get":{"tags":["Courses"],"summary":"Get course details","description":"Get full course details including modules and lessons","security":[],"parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"200":{"description":"Course details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Course"}}}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}},"patch":{"tags":["Courses"],"summary":"Update a course","description":"Update course details (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCourseRequest"}}}},"responses":{"200":{"description":"Course updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Course"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}},"delete":{"tags":["Courses"],"summary":"Delete a course","description":"Delete a course and all its modules/lessons (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"204":{"description":"Course deleted successfully"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/courses/{id}/publish":{"patch":{"tags":["Courses"],"summary":"Toggle course publish status","description":"Publish or unpublish a course (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["published"],"properties":{"published":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Publish status updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Course"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/courses/{courseId}/modules":{"get":{"tags":["Modules"],"summary":"List modules for a course","description":"Get all modules for a specific course","parameters":[{"$ref":"#/components/parameters/CourseIdParam"}],"responses":{"200":{"description":"List of modules","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Module"}}}}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}},"post":{"tags":["Modules"],"summary":"Create a module","description":"Create a new module in a course (admin only)","parameters":[{"$ref":"#/components/parameters/CourseIdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModuleRequest"}}}},"responses":{"201":{"description":"Module created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Module"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/modules/{id}":{"patch":{"tags":["Modules"],"summary":"Update a module","description":"Update module details (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"order":{"type":"integer"}}}}}},"responses":{"200":{"description":"Module updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Module"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}},"delete":{"tags":["Modules"],"summary":"Delete a module","description":"Delete a module and all its lessons (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"204":{"description":"Module deleted successfully"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/modules/reorder":{"patch":{"tags":["Modules"],"summary":"Reorder modules","description":"Update the order of modules within a course (admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["courseId","moduleOrders"],"properties":{"courseId":{"type":"string"},"moduleOrders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"order":{"type":"integer"}}}}}}}}},"responses":{"200":{"description":"Modules reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Modules reordered successfully"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/modules/{moduleId}/lessons":{"get":{"tags":["Lessons"],"summary":"List lessons for a module","description":"Get all lessons for a specific module","parameters":[{"$ref":"#/components/parameters/ModuleIdParam"}],"responses":{"200":{"description":"List of lessons","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Lesson"}}}}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}},"post":{"tags":["Lessons"],"summary":"Create a lesson","description":"Create a new lesson in a module (teacher/admin only)","parameters":[{"$ref":"#/components/parameters/ModuleIdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLessonRequest"}}}},"responses":{"201":{"description":"Lesson created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Lesson"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/lessons/{id}":{"get":{"tags":["Lessons"],"summary":"Get lesson details","description":"Get full lesson details including materials and progress (for enrolled students)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"200":{"description":"Lesson details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"allOf":[{"$ref":"#/components/schemas/Lesson"},{"type":"object","properties":{"progress":{"$ref":"#/components/schemas/LessonProgress"}}}]}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"description":"Not enrolled in this course","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}},"patch":{"tags":["Lessons"],"summary":"Update a lesson","description":"Update lesson details (teacher/admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"textContent":{"type":"string"},"order":{"type":"integer"}}}}}},"responses":{"200":{"description":"Lesson updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Lesson"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}},"delete":{"tags":["Lessons"],"summary":"Delete a lesson","description":"Delete a lesson and all its materials (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"204":{"description":"Lesson deleted successfully"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/lessons/reorder":{"patch":{"tags":["Lessons"],"summary":"Reorder lessons","description":"Update the order of lessons within a module (admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["moduleId","lessonOrders"],"properties":{"moduleId":{"type":"string"},"lessonOrders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"order":{"type":"integer"}}}}}}}}},"responses":{"200":{"description":"Lessons reordered successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Lessons reordered successfully"}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/lessons/{id}/materials":{"post":{"tags":["Lessons"],"summary":"Upload lesson material","description":"Upload a material file for a lesson (teacher/admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","title"],"properties":{"file":{"type":"string","format":"binary","description":"Material file (PDF, Word, max 50MB)"},"title":{"type":"string","description":"Material title"}}}}}},"responses":{"201":{"description":"Material uploaded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/LessonMaterial"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/lessons/{id}/materials/{materialId}":{"delete":{"tags":["Lessons"],"summary":"Delete lesson material","description":"Delete a material from a lesson (admin only)","parameters":[{"$ref":"#/components/parameters/IdParam"},{"name":"materialId","in":"path","required":true,"schema":{"type":"string"},"description":"Material ID"}],"responses":{"204":{"description":"Material deleted successfully"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/payments/create":{"post":{"tags":["Payments"],"summary":"Create a payment request","description":"Initiate a payment for course enrollment. Returns a payment URL to redirect the user to the payment gateway.\n\n**Flow:**\n1. User selects course and payment method\n2. Backend creates order and initiates payment with gateway\n3. Returns payment URL\n4. User completes payment on gateway\n5. Gateway redirects back to callback URL\n6. Backend verifies payment and enrolls user\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentRequest"}}}},"responses":{"200":{"description":"Payment initiated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Invalid request or already enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"429":{"$ref":"#/components/responses/RateLimitError"}}}},"/payments/verify-coupon":{"post":{"tags":["Payments"],"summary":"Verify a coupon code","description":"Check if a coupon is valid and calculate the discount","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyCouponRequest"}}}},"responses":{"200":{"description":"Coupon verification result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"valid":{"type":"boolean","example":true},"coupon":{"$ref":"#/components/schemas/Coupon"},"originalPrice":{"type":"number","example":2999},"discountAmount":{"type":"number","example":599.8},"finalPrice":{"type":"number","example":2399.2}}}}}}}},"400":{"description":"Invalid or expired coupon","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Coupon has expired"}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/payments/{orderId}/status":{"get":{"tags":["Payments"],"summary":"Check payment status","description":"Get the current status of a payment/order","parameters":[{"name":"orderId","in":"path","required":true,"schema":{"type":"string"},"description":"Order ID"}],"responses":{"200":{"description":"Payment status","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"orderId":{"type":"string"},"status":{"type":"string","enum":["PENDING","COMPLETED","FAILED","REFUNDED"]},"transactionId":{"type":"string","nullable":true},"amount":{"type":"number"},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/payments/bkash/callback":{"post":{"tags":["Payments"],"summary":"bKash payment callback","description":"Callback endpoint for bKash payment gateway.\n\n**Note:** This endpoint is called by bKash, not by the client.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"paymentID":{"type":"string"},"status":{"type":"string","enum":["success","failure","cancel"]},"trxID":{"type":"string"},"amount":{"type":"string"},"currency":{"type":"string"},"merchantInvoiceNumber":{"type":"string"}}}}}},"responses":{"302":{"description":"Redirect to success or failure page"},"400":{"description":"Invalid callback data"}}}},"/payments/nagad/callback":{"post":{"tags":["Payments"],"summary":"Nagad payment callback","description":"Callback endpoint for Nagad payment gateway.\n\n**Note:** This endpoint is called by Nagad, not by the client.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"merchant":{"type":"string"},"order_id":{"type":"string"},"payment_ref_id":{"type":"string"},"status":{"type":"string"},"status_code":{"type":"string"},"amount":{"type":"string"}}}}}},"responses":{"302":{"description":"Redirect to success or failure page"},"400":{"description":"Invalid callback data"}}}},"/payments/ssl/callback":{"post":{"tags":["Payments"],"summary":"SSLCommerz payment callback","description":"Callback endpoint for SSLCommerz payment gateway.\n\n**Note:** This endpoint is called by SSLCommerz, not by the client.\n","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"tran_id":{"type":"string"},"val_id":{"type":"string"},"amount":{"type":"string"},"card_type":{"type":"string"},"store_amount":{"type":"string"},"card_no":{"type":"string"},"bank_tran_id":{"type":"string"},"status":{"type":"string","enum":["VALID","FAILED","CANCELLED"]}}}}}},"responses":{"302":{"description":"Redirect to success or failure page"},"400":{"description":"Invalid callback data"}}}},"/progress/lessons/{lessonId}":{"post":{"tags":["Progress"],"summary":"Update lesson watch progress","description":"Update the watch progress for a lesson. Called periodically by the video player.\n\nProgress is used to:\n- Calculate overall course completion percentage\n- Enable resume from last position\n- Trigger certificate generation on 100% completion\n","parameters":[{"$ref":"#/components/parameters/LessonIdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProgressRequest"}}}},"responses":{"200":{"description":"Progress updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"lessonProgress":{"$ref":"#/components/schemas/LessonProgress"},"courseProgress":{"type":"object","properties":{"percentage":{"type":"number","example":45.5},"completed":{"type":"boolean","example":false}}}}}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"description":"Not enrolled in this course","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/progress/courses/{courseId}":{"get":{"tags":["Progress"],"summary":"Get course progress","description":"Get detailed progress for a specific course including all lesson progress","parameters":[{"$ref":"#/components/parameters/CourseIdParam"}],"responses":{"200":{"description":"Course progress details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"enrollment":{"$ref":"#/components/schemas/Enrollment"},"progressPercentage":{"type":"number","example":45.5},"completedLessons":{"type":"integer","example":5},"totalLessons":{"type":"integer","example":12},"totalWatchTime":{"type":"integer","description":"Total watch time in seconds","example":3600},"lessonProgress":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/LessonProgress"},{"type":"object","properties":{"lesson":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"duration":{"type":"integer"}}}}}]}}}}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"description":"Not enrolled in this course","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/progress/my-courses":{"get":{"tags":["Progress"],"summary":"Get all enrolled courses with progress","description":"Get a list of all courses the user is enrolled in with their progress","parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"status","in":"query","schema":{"type":"string","enum":["in_progress","completed"]},"description":"Filter by completion status"}],"responses":{"200":{"description":"List of enrolled courses with progress","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"enrollment":{"$ref":"#/components/schemas/Enrollment"},"course":{"$ref":"#/components/schemas/Course"},"progressPercentage":{"type":"number"},"completedAt":{"type":"string","format":"date-time","nullable":true},"certificateUrl":{"type":"string","nullable":true},"lastAccessedAt":{"type":"string","format":"date-time"}}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/progress/leaderboard":{"get":{"tags":["Progress"],"summary":"Get leaderboard","description":"Get the top students ranked by engagement.\n\nRanking is based on:\n- Courses completed (weighted)\n- Total watch time\n- Consistent learning streaks\n","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":50},"description":"Number of entries to return"},{"name":"period","in":"query","schema":{"type":"string","enum":["week","month","all_time"],"default":"month"},"description":"Time period for ranking"}],"responses":{"200":{"description":"Leaderboard entries","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/LeaderboardEntry"}}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/certificates/{enrollmentId}":{"get":{"tags":["Progress"],"summary":"Download certificate","description":"Download the completion certificate for a course (PDF)","parameters":[{"name":"enrollmentId","in":"path","required":true,"schema":{"type":"string"},"description":"Enrollment ID"}],"responses":{"200":{"description":"Certificate PDF","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Course not completed yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"error":{"code":"VALIDATION_ERROR","message":"Certificate not available. Please complete the course first."}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/public/homepage":{"get":{"tags":["Public"],"summary":"Get homepage data","description":"Get all data needed for the homepage (hero, featured courses, testimonials, etc.)","security":[],"responses":{"200":{"description":"Homepage data","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"featuredCourses":{"type":"array","items":{"$ref":"#/components/schemas/Course"}},"testimonials":{"type":"array","items":{"$ref":"#/components/schemas/Testimonial"}},"stats":{"type":"object","properties":{"totalStudents":{"type":"integer","example":500},"totalCourses":{"type":"integer","example":10},"totalLessons":{"type":"integer","example":150}}}}}}}}}}}}},"/public/courses":{"get":{"tags":["Public"],"summary":"List published courses","description":"Get a paginated list of published courses for the public website","security":[],"parameters":[{"$ref":"#/components/parameters/PageParam"},{"$ref":"#/components/parameters/LimitParam"},{"name":"type","in":"query","schema":{"type":"string","enum":["ONLINE","OFFLINE"]},"description":"Filter by course type"},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search by title"}],"responses":{"200":{"description":"List of courses","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Course"}}}}]}}}}}}},"/public/courses/{id}":{"get":{"tags":["Public"],"summary":"Get course details","description":"Get public course details (no lesson videos for non-enrolled users)","security":[],"parameters":[{"$ref":"#/components/parameters/IdParam"}],"responses":{"200":{"description":"Course details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"allOf":[{"$ref":"#/components/schemas/Course"},{"type":"object","properties":{"totalLessons":{"type":"integer"},"totalDuration":{"type":"integer","description":"Total duration in seconds"},"enrolledCount":{"type":"integer"}}}]}}}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/public/books":{"get":{"tags":["Public"],"summary":"List book recommendations","description":"Get all book recommendations for the public website","security":[],"responses":{"200":{"description":"List of books","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Book"}}}}}}}}}},"/public/testimonials":{"get":{"tags":["Public"],"summary":"List testimonials","description":"Get all published testimonials","security":[],"responses":{"200":{"description":"List of testimonials","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Testimonial"}}}}}}}}}},"/public/faqs":{"get":{"tags":["Public"],"summary":"List FAQs","description":"Get all published FAQs","security":[],"parameters":[{"name":"category","in":"query","schema":{"type":"string"},"description":"Filter by category"}],"responses":{"200":{"description":"List of FAQs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"}}}}}}}}}},"/public/contact":{"post":{"tags":["Public"],"summary":"Submit contact form","description":"Submit a contact form message","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}}},"responses":{"201":{"description":"Contact form submitted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string","example":"Thank you for your message. We will get back to you soon."}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimitError"}}}},"/offline-registration":{"post":{"tags":["Offline Registration"],"summary":"Submit offline registration","description":"Submit a registration for offline courses.\n\nIf `paymentType` is `ONLINE`, will redirect to payment gateway.\nIf `paymentType` is `OFFLINE`, creates a pending registration for admin approval.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOfflineRegistrationRequest"}}}},"responses":{"201":{"description":"Registration submitted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"registration":{"$ref":"#/components/schemas/OfflineRegistration"},"paymentUrl":{"type":"string","nullable":true,"description":"Payment URL (only for online payment type)"},"message":{"type":"string","example":"Registration submitted successfully. Please complete the payment."}}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"404":{"description":"Course not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/videos/upload-url":{"post":{"tags":["Videos"],"summary":"Get video upload URL","description":"Get a signed upload URL from BunnyCDN (teacher/admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","lessonId"],"properties":{"title":{"type":"string","description":"Video title"},"lessonId":{"type":"string","description":"Lesson ID to associate the video with"}}}}}},"responses":{"200":{"description":"Upload URL generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoUploadUrlResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}},"/videos/webhook":{"post":{"tags":["Videos"],"summary":"BunnyCDN webhook","description":"Webhook endpoint for BunnyCDN encoding status updates.\n\n**Note:** This endpoint is called by BunnyCDN, not by the client.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"VideoGuid":{"type":"string"},"Status":{"type":"integer","description":"Encoding status (3 = ready, 4 = encoding, 5 = failed)"},"Width":{"type":"integer"},"Height":{"type":"integer"},"Length":{"type":"integer","description":"Duration in seconds"}}}}}},"responses":{"200":{"description":"Webhook processed"},"400":{"description":"Invalid webhook data"}}}},"/videos/{videoId}/stream":{"get":{"tags":["Videos"],"summary":"Get video stream URL","description":"Get the streaming URL for a video (authenticated, enrolled users only)","parameters":[{"name":"videoId","in":"path","required":true,"schema":{"type":"string"},"description":"BunnyCDN video ID"}],"responses":{"200":{"description":"Stream URL","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"embedUrl":{"type":"string","description":"iframe embed URL"},"hlsUrl":{"type":"string","description":"HLS streaming URL"}}}}}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"description":"Not enrolled in this course","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/videos/{videoId}":{"delete":{"tags":["Videos"],"summary":"Delete video","description":"Delete a video from BunnyCDN (admin only)","parameters":[{"name":"videoId","in":"path","required":true,"schema":{"type":"string"},"description":"BunnyCDN video ID"}],"responses":{"204":{"description":"Video deleted"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}