{"openapi":"3.0.0","paths":{"/api/v1/property":{"post":{"operationId":"PropertyController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePropertyDto"}}}},"responses":{"201":{"description":"Property created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Property"}}}},"403":{"description":"Forbidden - Agent or Admin role required"}},"security":[{"bearer":[]}],"summary":"Create a new property (Agent or Admin)","tags":["property"]},"get":{"operationId":"PropertyController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (pagination)","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (pagination)","schema":{"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort directive, e.g., createdAt:DESC","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","description":"Property status filter","schema":{"example":"approved","type":"string","enum":["draft","pending_approval","approved","rejected","inactive"]}},{"name":"type","required":false,"in":"query","description":"Property type filter","schema":{"example":"apartment","type":"string"}},{"name":"category","required":false,"in":"query","description":"Listing category (rental, sale, or shortlet)","schema":{"example":"rental","type":"string","enum":["rental","sale","shortlet"]}},{"name":"location","required":false,"in":"query","description":"Location filter","schema":{"example":"Lekki Phase 1, Lagos","type":"string"}},{"name":"propertySize","required":false,"in":"query","description":"Number of bedrooms (2, 3, 4, 5, or 6)","schema":{"example":"4_bedroom","type":"string","enum":["self_contained","studio","1_bedroom","2_bedroom","3_bedroom","4_bedroom","5_bedroom","duplex","bungalow","semi_detached","terraced","detached"]}},{"name":"furnishing","required":false,"in":"query","description":"Furnishing type filter","schema":{"example":"furnished","type":"string","enum":["furnished","semi_furnished","unfurnished"]}},{"name":"minAmount","required":false,"in":"query","description":"Minimum amount in Naira","schema":{"example":1000000,"type":"number"}},{"name":"maxAmount","required":false,"in":"query","description":"Maximum amount in Naira","schema":{"example":100000000,"type":"number"}}],"responses":{"200":{"description":"Properties retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Property"}}}}}},"summary":"Get all properties with optional filters","tags":["property"]}},"/api/v1/property/approved":{"get":{"operationId":"PropertyController_findApproved","parameters":[],"responses":{"200":{"description":"Approved properties retrieved","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Property"}}}}}},"summary":"Get all approved properties (public)","tags":["property"]}},"/api/v1/property/search/global":{"get":{"operationId":"PropertyController_searchProperties","parameters":[{"name":"q","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Search results retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Property"}}}}}},"summary":"Global search for properties","tags":["property"]}},"/api/v1/property/my-properties":{"get":{"operationId":"PropertyController_findMyProperties","parameters":[],"responses":{"200":{"description":"Agent properties retrieved","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Property"}}}}}},"security":[{"bearer":[]}],"summary":"Get all properties of the authenticated agent","tags":["property"]}},"/api/v1/property/saved":{"get":{"operationId":"PropertyController_getSavedProperties","parameters":[],"responses":{"200":{"description":""}},"summary":"Get saved properties for the current user or guest","tags":["property"]}},"/api/v1/property/{id}":{"get":{"operationId":"PropertyController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Property retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Property"}}}}},"summary":"Get a specific property by ID","tags":["property"]},"patch":{"operationId":"PropertyController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePropertyDto"}}}},"responses":{"200":{"description":"Property updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Property"}}}}},"security":[{"bearer":[]}],"summary":"Update a property (Agent - own properties only, Admin - any property)","tags":["property"]},"delete":{"operationId":"PropertyController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Property deleted successfully"}},"security":[{"bearer":[]}],"summary":"Delete a property (Agent - own properties only)","tags":["property"]}},"/api/v1/property/recently-viewed/me":{"get":{"operationId":"PropertyController_getMyRecentlyViewed","parameters":[],"responses":{"200":{"description":""}},"summary":"Get recently viewed properties for the current user or guest","tags":["property"]}},"/api/v1/property/{id}/save":{"post":{"operationId":"PropertyController_saveProperty","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":""}},"summary":"Save a property for the current user or guest","tags":["property"]},"delete":{"operationId":"PropertyController_unsaveProperty","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Remove a property from the current user or guest's saved list","tags":["property"]}},"/api/v1/property/{id}/agreement/me":{"get":{"operationId":"PropertyController_getMyAgreement","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get the current buyer agreement status for a property","tags":["property"]}},"/api/v1/property/{id}/agreement/sign":{"post":{"operationId":"PropertyController_signAgreement","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignPropertyAgreementDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Sign the property purchase agreement before payment","tags":["property"]}},"/api/v1/property/{id}/bookings":{"post":{"operationId":"PropertyController_createBooking","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePropertyBookingDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a booking for a property (authenticated users only)","tags":["property"]},"get":{"operationId":"PropertyController_listBookings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List bookings for a property (Admin/Agent only)","tags":["property"]}},"/api/v1/property/bookings/my-bookings":{"get":{"operationId":"PropertyController_getMyBookings","parameters":[],"responses":{"200":{"description":"User bookings retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get all bookings for the authenticated user","tags":["property"]}},"/api/v1/upload":{"post":{"description":"Generic upload endpoint. Accepts single or multiple files of any supported type (images, videos). Returns asset IDs that can be attached to entities later.","operationId":"UploadController_uploadFiles","parameters":[],"requestBody":{"required":true,"description":"Files to upload (mixed types supported)","content":{"multipart/form-data":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string","format":"binary"}}}}}}},"responses":{"201":{"description":"Files uploaded successfully","content":{"application/json":{"schema":{"type":"object","properties":{"assets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"path":{"type":"string"},"type":{"type":"string","enum":["image","video"]},"mimeType":{"type":"string"},"size":{"type":"number"},"url":{"type":"string"}}}}}}}}}},"security":[{"bearer":[]}],"summary":"Upload files (images/videos)","tags":["upload"]}},"/api/v1/upload/request-presigned-urls":{"post":{"description":"Get pre-signed URLs to upload files directly to Cloudflare R2. Frontend uploads files to R2, then confirms with /upload/confirm endpoint.","operationId":"UploadController_requestPresignedUrls","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestPresignedUrlsDto"}}}},"responses":{"200":{"description":"Pre-signed URLs generated successfully","content":{"application/json":{"schema":{"example":{"uploads":[{"mode":"single","assetId":1,"uploadUrl":"https://<account>.r2.cloudflarestorage.com/my-bucket/images/123-abc.jpg?signature=...","key":"images/123-abc.jpg"},{"mode":"multipart","assetId":2,"uploadId":"abc123","partSize":8388608,"key":"videos/124-def.mp4","parts":[{"partNumber":1,"uploadUrl":"https://...&partNumber=1&uploadId=abc123"},{"partNumber":2,"uploadUrl":"https://...&partNumber=2&uploadId=abc123"}]}],"message":"Pre-signed URLs generated successfully"}}}}},"400":{"description":"Invalid file metadata"}},"security":[{"bearer":[]}],"summary":"Request pre-signed URLs for direct Cloudflare R2 upload","tags":["upload"]}},"/api/v1/upload/confirm":{"post":{"description":"After uploading files to Cloudflare R2 using pre-signed URLs, call this endpoint to mark uploads as completed.","operationId":"UploadController_confirmUploads","parameters":[],"requestBody":{"required":true,"description":"List of uploads to finalise. Include uploadId and parts only for multipart uploads.","content":{"application/json":{"schema":{"example":{"uploads":[{"assetId":101},{"assetId":102,"uploadId":"NFcQz8VrYMU7M2L2Fk9iXbE7hRq9pMx5vM6jAl","parts":[{"partNumber":1,"eTag":"\"etag-part-1\""},{"partNumber":2,"eTag":"\"etag-part-2\""}]}]}}}}},"responses":{"200":{"description":"Uploads confirmed successfully","content":{"application/json":{"schema":{"example":{"message":"Uploads confirmed successfully"}}}}},"400":{"description":"Asset not found or file not in Cloudflare R2"}},"security":[{"bearer":[]}],"summary":"Confirm successful uploads to Cloudflare R2","tags":["upload"]}},"/api/v1/notification":{"get":{"operationId":"NotificationController_getUserNotifications","parameters":[{"name":"unreadOnly","required":false,"in":"query","description":"Filter to show only unread notifications","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Notifications retrieved successfully"}},"security":[{"accessToken":[]}],"summary":"Get user notifications with pagination","tags":["Notifications"]}},"/api/v1/notification/unread-count":{"get":{"operationId":"NotificationController_getUnreadCount","parameters":[],"responses":{"200":{"description":"Unread count retrieved successfully"}},"security":[{"accessToken":[]}],"summary":"Get unread notification count","tags":["Notifications"]}},"/api/v1/notification/{id}/read":{"patch":{"operationId":"NotificationController_markAsRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Notification marked as read"}},"security":[{"accessToken":[]}],"summary":"Mark notification as read","tags":["Notifications"]}},"/api/v1/notification/read-all":{"patch":{"operationId":"NotificationController_markAllAsRead","parameters":[],"responses":{"200":{"description":"All notifications marked as read"}},"security":[{"accessToken":[]}],"summary":"Mark all notifications as read","tags":["Notifications"]}},"/api/v1/notification/{id}":{"delete":{"operationId":"NotificationController_deleteNotification","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Notification deleted successfully"}},"security":[{"accessToken":[]}],"summary":"Delete notification","tags":["Notifications"]}},"/api/v1/payment/initialize":{"post":{"description":"Unified endpoint for initializing payments. Supports inspections, consultations, orders, and properties.","operationId":"PaymentController_initializeEntityPayment","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitializeEntityPaymentDto"}}}},"responses":{"200":{"description":"Payment initialized successfully","content":{"application/json":{"schema":{"example":{"message":"Success","data":{"reference":"PAY_ORD_1_1700000000000","authorizationUrl":"https://checkout.paystack.com/xyz","accessCode":"xyz123","provider":"paystack","amount":150000,"currency":"NGN","paymentType":"ORDER"}}}}}},"400":{"description":"Invalid payment request"}},"security":[{"bearer":[]}],"summary":"Initialize payment for an entity","tags":["Payment"]}},"/api/v1/payment/verify/{reference}":{"get":{"operationId":"PaymentController_verifyPayment","parameters":[{"name":"reference","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Payment verified successfully","content":{"application/json":{"schema":{"example":{"message":"Success","data":{"success":true,"reference":"PAY_1234567890_ABC","amount":500,"currency":"NGN","status":"success","paidAt":"2024-01-15T10:30:00.000Z","customer":{"email":"customer@example.com","firstName":"John","lastName":"Doe"},"metadata":{"orderId":123}}}}}}},"400":{"description":"Bad request - invalid reference or verification failed"},"401":{"description":"Unauthorized - authentication required"}},"security":[{"bearer":[]}],"summary":"Verify a payment transaction","tags":["Payment"]}},"/api/v1/payment/details/{reference}":{"get":{"operationId":"PaymentController_getPaymentDetails","parameters":[{"name":"reference","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Payment details retrieved successfully"},"400":{"description":"Bad request - invalid reference"},"401":{"description":"Unauthorized - authentication required"}},"security":[{"bearer":[]}],"summary":"Get payment transaction details","tags":["Payment"]}},"/api/v1/payment/callback":{"get":{"description":"Redirects user back to frontend after payment attempt. Does not process payment - webhook handles that.","operationId":"PaymentController_paymentCallback","parameters":[{"name":"reference","required":true,"in":"query","schema":{"type":"string"}},{"name":"tx_ref","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to frontend with payment reference"},"400":{"description":"Missing payment reference"}},"summary":"Payment callback redirect (user-facing only)","tags":["Payment"]}},"/api/v1/payment/webhook":{"post":{"description":"Receives POST notifications from Paystack/Flutterwave when payment status changes. Signature verified.","operationId":"PaymentController_paymentWebhook","parameters":[],"responses":{"200":{"description":"Webhook processed successfully"},"401":{"description":"Invalid webhook signature"}},"summary":"Webhook endpoint for payment provider notifications","tags":["Payment"]}},"/api/v1/payment/transaction/{reference}":{"get":{"operationId":"PaymentController_getTransaction","parameters":[{"name":"reference","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Transaction retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get payment transaction details","tags":["Payment"]}},"/api/v1/users/guests":{"post":{"description":"Generate a guest identifier that can be used to track unauthenticated activity (wishlist, cart, recents).","operationId":"UsersController_createGuest","parameters":[],"responses":{"201":{"description":"Guest ID generated successfully","content":{"application/json":{"schema":{"example":{"guestId":123456}}}}}},"summary":"Create guest session","tags":["Users"]}},"/api/v1/users/profile":{"get":{"description":"Retrieve full profile information for authenticated user including metadata and linked auth providers","operationId":"UsersController_getProfile","parameters":[],"responses":{"200":{"description":"User profile retrieved successfully","content":{"application/json":{"schema":{"example":{"id":1,"email":"john@example.com","firstName":"John","lastName":"Doe","phone":"+2348012345678","role":"user","status":"active","userMeta":{"gender":"male","dateOfBirth":"1990-01-15","city":"Lagos","country":"Nigeria"},"authProviders":["local","google"]}}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"}},"security":[{"accessToken":[]}],"summary":"Get current user profile","tags":["Users"]}},"/api/v1/users/me":{"get":{"description":"Alternative endpoint to retrieve authenticated user profile. Same as /users/profile.","operationId":"UsersController_getMe","parameters":[],"responses":{"200":{"description":"User profile retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"}},"security":[{"accessToken":[]}],"summary":"Get current user (alias)","tags":["Users"]}},"/api/v1/users":{"post":{"description":"Create a new user account. This endpoint is for admin use to create users directly.","operationId":"UsersController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"User created successfully","content":{"application/json":{"schema":{"example":{"id":1,"email":"newuser@example.com","firstName":"Jane","lastName":"Smith","role":"user","status":"active"}}}}},"400":{"description":"Invalid input data"},"409":{"description":"Email already exists"}},"summary":"Create new user (Admin)","tags":["Users"]},"get":{"description":"Retrieve list of all users. Admin only endpoint.","operationId":"UsersController_findAll","parameters":[],"responses":{"200":{"description":"List of users retrieved successfully","content":{"application/json":{"schema":{"example":[{"id":1,"email":"john@example.com","firstName":"John","lastName":"Doe","role":"user","status":"active"},{"id":2,"email":"jane@example.com","firstName":"Jane","lastName":"Smith","role":"agent","status":"active"}]}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - Admin access required"}},"security":[{"accessToken":[]}],"summary":"Get all users (Admin)","tags":["Users"]}},"/api/v1/users/{id}":{"get":{"description":"Retrieve specific user information by user ID.","operationId":"UsersController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":1,"type":"number"}}],"responses":{"200":{"description":"User found","content":{"application/json":{"schema":{"example":{"id":1,"email":"john@example.com","firstName":"John","lastName":"Doe","role":"user","status":"active","userMeta":{"city":"Lagos","country":"Nigeria"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"User not found"}},"security":[{"accessToken":[]}],"summary":"Get user by ID","tags":["Users"]},"patch":{"description":"Update user information. Users can update their own profile, admins can update any user.","operationId":"UsersController_update","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":1,"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"User updated successfully","content":{"application/json":{"schema":{"example":{"id":1,"email":"john@example.com","firstName":"John","lastName":"Doe Updated","role":"user","status":"active"}}}}},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - Cannot update other users"},"404":{"description":"User not found"}},"security":[{"accessToken":[]}],"summary":"Update user","tags":["Users"]}},"/api/v1/billing/profile":{"get":{"operationId":"BillingProfileController_getProfile","parameters":[],"responses":{"200":{"description":"Billing profile retrieved successfully"},"404":{"description":"Billing profile not found"}},"security":[{"bearer":[]}],"summary":"Fetch billing profile for the authenticated user","tags":["Billing"]},"put":{"operationId":"BillingProfileController_upsertProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertBillingProfileDto"}}}},"responses":{"200":{"description":"Billing profile saved successfully"}},"security":[{"bearer":[]}],"summary":"Create or update the authenticated user billing profile","tags":["Billing"]}},"/api/v1/auth/register":{"post":{"description":"Create a new user account with email and password. User must login separately after registration.","operationId":"AuthController_register","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"User successfully registered","content":{"application/json":{"schema":{"example":{"message":"Registration successful","user":{"id":1,"email":"john@example.com","firstName":"John","lastName":"Doe","role":"user"}}}}}},"400":{"description":"Invalid input or email already exists"}},"summary":"Register new user","tags":["Auth"]}},"/api/v1/auth/signup/otp":{"post":{"description":"Send verification OTP to email. Use this OTP when calling the standard /auth/register endpoint.","operationId":"AuthController_signupRequest","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequestDto"}}}},"responses":{"200":{"description":"OTP sent to email"},"400":{"description":"Email already registered"}},"summary":"Request OTP for signup","tags":["Auth"]}},"/api/v1/auth/signup/verify-otp":{"post":{"description":"Verify the OTP sent to email during signup. Required before registration can proceed.","operationId":"AuthController_verifyOtp","parameters":[],"responses":{"200":{"description":"Email verified successfully","content":{"application/json":{"schema":{"example":{"message":"Email verified successfully","email":"user@example.com"}}}}},"400":{"description":"Invalid or expired OTP"},"404":{"description":"OTP not found"}},"summary":"Verify email with OTP token","tags":["Auth"]}},"/api/v1/auth/login":{"post":{"description":"Authenticate using email/password credentials. Returns JWT access token.","operationId":"AuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginUserDto"}}}},"responses":{"200":{"description":"Login successful","content":{"application/json":{"schema":{"example":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","user":{"id":1,"email":"john@example.com","firstName":"John","lastName":"Doe","role":"user"}}}}}},"401":{"description":"Invalid credentials"}},"summary":"Login with email and password","tags":["Auth"],"x-postman-test":"pm.test(\"Status code is 200\", function () {\n    pm.response.to.have.status(200);\n});\n\npm.test(\"Response has access_token\", function () {\n    var jsonData = pm.response.json();\n    pm.expect(jsonData).to.have.property('access_token');\n});\n\npm.test(\"Response has user object\", function () {\n    var jsonData = pm.response.json();\n    pm.expect(jsonData).to.have.property('user');\n    pm.expect(jsonData.user).to.have.property('email');\n});\n\nvar jsonData = pm.response.json();\nif (jsonData.access_token) {\n    pm.environment.set(\"accessToken\", jsonData.access_token);\n    pm.environment.set(\"token\", jsonData.access_token);\n    console.log(\"✅ Access token saved to environment\");\n}\n\nif (jsonData.user) {\n    pm.environment.set(\"userId\", jsonData.user.id);\n    pm.environment.set(\"userEmail\", jsonData.user.email);\n    pm.environment.set(\"userRole\", jsonData.user.role);\n    console.log(\"✅ User details saved to environment\");\n}"}},"/api/v1/auth/forgot-password":{"post":{"description":"Send OTP code to user email for password reset. OTP expires in 10 minutes.","operationId":"AuthController_forgotPassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordDto"}}}},"responses":{"200":{"description":"OTP sent successfully","content":{"application/json":{"schema":{"example":{"message":"Password reset OTP sent to your email"}}}}},"404":{"description":"User not found"}},"summary":"Request password reset","tags":["Auth"]}},"/api/v1/auth/reset-password":{"post":{"description":"Confirm password reset using OTP code sent to email. Sets new password.","operationId":"AuthController_resetPassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordDto"}}}},"responses":{"200":{"description":"Password reset successful","content":{"application/json":{"schema":{"example":{"message":"Password reset successfully"}}}}},"400":{"description":"Invalid or expired OTP"},"404":{"description":"User not found"}},"summary":"Reset password with OTP","tags":["Auth"]}},"/api/v1/auth/change-password":{"post":{"description":"Change password for authenticated user. Requires current password and new password.","operationId":"AuthController_changePassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordDto"}}}},"responses":{"200":{"description":"Password changed successfully","content":{"application/json":{"schema":{"example":{"message":"Password has been changed successfully"}}}}},"400":{"description":"Invalid current password"},"401":{"description":"Unauthorized - Invalid or missing JWT token"}},"security":[{"accessToken":[]}],"summary":"Change password","tags":["Auth"]}},"/api/v1/auth/oauth/{provider}":{"get":{"description":"Redirect user to OAuth provider (Google or Facebook) for authentication. Supports dynamic provider routing.","operationId":"AuthController_oauthAuth","parameters":[{"name":"provider","required":true,"in":"path","description":"OAuth provider name","schema":{"enum":["google","facebook"],"type":"string"}}],"responses":{"302":{"description":"Redirects to OAuth provider login page"},"400":{"description":"Unsupported OAuth provider"}},"summary":"Initiate OAuth login","tags":["Auth"]}},"/api/v1/auth/oauth/{provider}/callback":{"get":{"description":"Handles OAuth callback from provider. Creates or links user account, returns JWT token.","operationId":"AuthController_oauthCallback","parameters":[{"name":"provider","required":true,"in":"path","description":"OAuth provider name","schema":{"enum":["google","facebook"],"type":"string"}}],"responses":{"200":{"description":"OAuth authentication successful","content":{"application/json":{"schema":{"example":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","user":{"id":1,"email":"john@gmail.com","firstName":"John","lastName":"Doe","role":"user","linkedProviders":["local","google"]}}}}}},"401":{"description":"OAuth authentication failed"}},"summary":"OAuth callback endpoint","tags":["Auth"]}},"/api/v1/categories":{"post":{"operationId":"CategoriesController_createCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryDto"}}}},"responses":{"201":{"description":"Category created successfully"}},"summary":"Create new category","tags":["Categories"]},"get":{"operationId":"CategoriesController_findAllCategories","parameters":[],"responses":{"200":{"description":"Returns paginated categories"}},"summary":"Get all categories","tags":["Categories"]}},"/api/v1/categories/{id}":{"patch":{"operationId":"CategoriesController_updateCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryDto"}}}},"responses":{"200":{"description":"Category updated successfully"}},"summary":"Update category","tags":["Categories"]},"delete":{"operationId":"CategoriesController_deleteCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Category deleted successfully"},"404":{"description":"Category not found"}},"summary":"Delete category","tags":["Categories"]},"get":{"operationId":"CategoriesController_findCategoryById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Returns category details"},"404":{"description":"Category not found"}},"summary":"Get category by ID","tags":["Categories"]}},"/api/v1/categories/subcategories":{"post":{"operationId":"CategoriesController_createSubCategory","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubCategoryDto"}}}},"responses":{"201":{"description":"Subcategory created successfully"}},"summary":"Create new subcategory","tags":["Categories"]},"get":{"operationId":"CategoriesController_findAllSubCategories","parameters":[],"responses":{"200":{"description":"Returns paginated subcategories"}},"summary":"Get all subcategories","tags":["Categories"]}},"/api/v1/categories/subcategories/{id}":{"get":{"operationId":"CategoriesController_findSubCategoryById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Returns subcategory details"},"404":{"description":"Subcategory not found"}},"summary":"Get subcategory by ID","tags":["Categories"]},"patch":{"operationId":"CategoriesController_updateSubCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSubCategoryDto"}}}},"responses":{"200":{"description":"Subcategory updated successfully"}},"summary":"Update subcategory","tags":["Categories"]},"delete":{"operationId":"CategoriesController_deleteSubCategory","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Subcategory deleted successfully"}},"summary":"Delete subcategory","tags":["Categories"]}},"/api/v1/products":{"post":{"operationId":"ProductsController_createProduct","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductDto"}}}},"responses":{"201":{"description":"Product successfully created"},"400":{"description":"Bad request"},"404":{"description":"SubCategory not found"}},"summary":"Create a new product","tags":["products"]},"get":{"operationId":"ProductsController_findAllProducts","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (pagination)","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page (pagination)","schema":{"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort directive, e.g., createdAt:DESC","schema":{"type":"string"}},{"name":"minPrice","required":false,"in":"query","description":"Minimum price filter","schema":{"example":0,"type":"number"}},{"name":"maxPrice","required":false,"in":"query","description":"Maximum price filter","schema":{"example":550000,"type":"number"}},{"name":"minRating","required":false,"in":"query","description":"Minimum rating filter (1-5)","schema":{"example":1,"type":"number"}},{"name":"maxRating","required":false,"in":"query","description":"Maximum rating filter (1-5)","schema":{"example":5,"type":"number"}}],"responses":{"200":{"description":"Products retrieved successfully"}},"summary":"Get all products","tags":["products"]}},"/api/v1/products/search/global":{"get":{"operationId":"ProductsController_searchProducts","parameters":[{"name":"q","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Search results retrieved successfully"}},"summary":"Global search for products","tags":["products"]}},"/api/v1/products/recently-viewed/me":{"get":{"operationId":"ProductsController_getMyRecentlyViewed","parameters":[],"responses":{"200":{"description":""}},"summary":"Get recently viewed products for the current user or guest","tags":["products"]}},"/api/v1/products/saved":{"get":{"operationId":"ProductsController_getSavedProducts","parameters":[],"responses":{"200":{"description":""}},"summary":"Get saved products for the current user or guest","tags":["products"]}},"/api/v1/products/highlights":{"get":{"operationId":"ProductsController_getHighlights","parameters":[{"name":"section","required":true,"in":"query","description":"Highlight section to fetch","schema":{"example":"best_seller","type":"string","enum":["best_seller","hot_deals","top_rated","hero"]}},{"name":"limit","required":false,"in":"query","description":"Maximum number of products to return","schema":{"example":8,"type":"number"}},{"name":"subCategoryId","required":false,"in":"query","description":"Optional subcategory filter","schema":{"example":12,"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Get product highlights for a homepage section","tags":["products"]}},"/api/v1/products/{id}/save":{"post":{"operationId":"ProductsController_saveProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":""}},"summary":"Save a product for the current user or guest","tags":["products"]},"delete":{"operationId":"ProductsController_unsaveProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"Remove a product from the current user or guest's saved list","tags":["products"]}},"/api/v1/products/{id}":{"get":{"operationId":"ProductsController_findProductById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Product retrieved successfully"},"404":{"description":"Product not found"}},"summary":"Get a product by ID","tags":["products"]},"patch":{"operationId":"ProductsController_updateProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductDto"}}}},"responses":{"200":{"description":"Product successfully updated"},"404":{"description":"Product not found"}},"summary":"Update a product","tags":["products"]},"delete":{"operationId":"ProductsController_deleteProduct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Product successfully deleted"},"404":{"description":"Product not found"}},"summary":"Delete a product (soft delete)","tags":["products"]}},"/api/v1/products/sub-category/{subCategoryId}":{"get":{"operationId":"ProductsController_findProductsBySubCategory","parameters":[{"name":"subCategoryId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Products retrieved successfully"}},"summary":"Get all products by subcategory","tags":["products"]}},"/api/v1/cart":{"get":{"operationId":"CartController_getCart","parameters":[{"name":"guestId","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"Cart retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get current user cart","tags":["cart"]},"delete":{"operationId":"CartController_clearCart","parameters":[{"name":"guestId","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"Cart cleared successfully"}},"security":[{"bearer":[]}],"summary":"Clear cart","tags":["cart"]}},"/api/v1/cart/add":{"post":{"operationId":"CartController_addToCart","parameters":[{"name":"guestId","required":true,"in":"query","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddToCartDto"}}}},"responses":{"200":{"description":"Product added to cart successfully"},"404":{"description":"Product not found"}},"security":[{"bearer":[]}],"summary":"Add product to cart","tags":["cart"]}},"/api/v1/cart/item/{productId}":{"patch":{"operationId":"CartController_updateCartItem","parameters":[{"name":"productId","required":true,"in":"path","schema":{"type":"number"}},{"name":"guestId","required":true,"in":"query","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCartItemDto"}}}},"responses":{"200":{"description":"Cart item updated successfully"},"404":{"description":"Product not found in cart"}},"security":[{"bearer":[]}],"summary":"Update cart item quantity","tags":["cart"]},"delete":{"operationId":"CartController_removeFromCart","parameters":[{"name":"productId","required":true,"in":"path","schema":{"type":"number"}},{"name":"guestId","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"Product removed from cart successfully"},"404":{"description":"Product not found in cart"}},"security":[{"bearer":[]}],"summary":"Remove product from cart","tags":["cart"]}},"/api/v1/orders/checkout":{"post":{"operationId":"OrdersController_checkout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutDto"}}}},"responses":{"201":{"description":"Order created successfully from cart"},"400":{"description":"Cart is empty or invalid data"},"404":{"description":"Product not found"}},"security":[{"bearer":[]}],"summary":"Checkout - Create order from cart","tags":["orders"]}},"/api/v1/orders":{"get":{"operationId":"OrdersController_findAllOrders","parameters":[],"responses":{"200":{"description":"Orders retrieved successfully"},"403":{"description":"Forbidden - Admin access required"}},"security":[{"bearer":[]}],"summary":"Get all orders (admin only)","tags":["orders"]}},"/api/v1/orders/my-orders":{"get":{"operationId":"OrdersController_findUserOrders","parameters":[],"responses":{"200":{"description":"User orders retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get current user orders","tags":["orders"]}},"/api/v1/orders/{id}":{"get":{"operationId":"OrdersController_findOrderById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Order retrieved successfully"},"404":{"description":"Order not found"}},"summary":"Get order by ID","tags":["orders"]},"patch":{"operationId":"OrdersController_updateOrder","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrderDto"}}}},"responses":{"200":{"description":"Order successfully updated"},"403":{"description":"Forbidden - Admin access required"},"404":{"description":"Order not found"}},"security":[{"bearer":[]}],"summary":"Update order status (admin only)","tags":["orders"]}},"/api/v1/orders/{id}/cancel":{"patch":{"operationId":"OrdersController_cancelOrder","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Order successfully cancelled"},"400":{"description":"Cannot cancel this order"},"404":{"description":"Order not found"}},"security":[{"bearer":[]}],"summary":"Cancel order (user can only cancel own pending orders, admin can cancel any)","tags":["orders"]}},"/api/v1/reviews":{"post":{"operationId":"ReviewsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReviewDto"}}}},"responses":{"201":{"description":"Review created successfully"},"400":{"description":"Bad request - Invalid data or duplicate review"},"401":{"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Create a new review","tags":["Reviews"]},"get":{"operationId":"ReviewsController_getAllReviews","parameters":[{"name":"type","required":false,"in":"query","description":"Filter by reviewable type","schema":{"enum":["product","property","agent"],"type":"string"}},{"name":"rating","required":false,"in":"query","description":"Filter by rating (1-5)","schema":{"type":"number"}}],"responses":{"200":{"description":"Paginated list of reviews"}},"summary":"Get all reviews","tags":["Reviews"]}},"/api/v1/reviews/my-reviews":{"get":{"operationId":"ReviewsController_getMyReviews","parameters":[],"responses":{"200":{"description":"List of user reviews"},"401":{"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Get all reviews created by the current user","tags":["Reviews"]}},"/api/v1/reviews/{id}":{"get":{"operationId":"ReviewsController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Review ID","schema":{"type":"number"}}],"responses":{"200":{"description":"Review details"},"404":{"description":"Review not found"}},"summary":"Get a specific review by ID","tags":["Reviews"]},"patch":{"operationId":"ReviewsController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Review ID","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReviewDto"}}}},"responses":{"200":{"description":"Review updated successfully"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - Not your review"},"404":{"description":"Review not found"}},"security":[{"bearer":[]}],"summary":"Update a review (own review only)","tags":["Reviews"]},"delete":{"operationId":"ReviewsController_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Review ID","schema":{"type":"number"}}],"responses":{"204":{"description":"Review deleted successfully"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden - Not your review"},"404":{"description":"Review not found"}},"security":[{"bearer":[]}],"summary":"Delete a review (own review only)","tags":["Reviews"]}},"/api/v1/chat":{"post":{"operationId":"ChatController_createChat","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatDto"}}}},"responses":{"201":{"description":"Chat created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}}},"security":[{"bearer":[]}],"summary":"Create a new chat with another user","tags":["chat"]}},"/api/v1/chat/my-chats":{"get":{"operationId":"ChatController_getMyChats","parameters":[],"responses":{"200":{"description":"Chats retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Chat"}}}}}},"security":[{"bearer":[]}],"summary":"Get all chats for the authenticated user","tags":["chat"]}},"/api/v1/chat/{id}":{"get":{"operationId":"ChatController_getChat","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Chat retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}}},"security":[{"bearer":[]}],"summary":"Get a specific chat by ID","tags":["chat"]},"patch":{"operationId":"ChatController_updateChat","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChatDto"}}}},"responses":{"200":{"description":"Chat updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}}},"security":[{"bearer":[]}],"summary":"Update chat status","tags":["chat"]}},"/api/v1/chat/{id}/messages":{"get":{"operationId":"ChatController_getChatMessages","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Messages retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}}}}}},"security":[{"bearer":[]}],"summary":"Get all messages in a chat","tags":["chat"]}},"/api/v1/chat/{id}/mark-read":{"post":{"operationId":"ChatController_markAsRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Messages marked as read"}},"security":[{"bearer":[]}],"summary":"Mark all messages in a chat as read","tags":["chat"]}},"/api/v1/chat/{id}/close":{"post":{"operationId":"ChatController_closeChat","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Chat closed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}}},"security":[{"bearer":[]}],"summary":"Close a chat","tags":["chat"]}},"/api/v1/admin/agent":{"post":{"operationId":"AdminController_createAgent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentDto"}}}},"responses":{"201":{"description":"Agent account created successfully"},"400":{"description":"Bad request - invalid data"},"401":{"description":"Unauthorized - admin access required"},"409":{"description":"Conflict - user with email already exists"}},"security":[{"bearer":[]}],"summary":"Create a new agent account","tags":["Admin"]}},"/api/v1/admin/interior-assets":{"post":{"operationId":"AdminController_updateInteriorAssets","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInteriorAssetsDto"}}}},"responses":{"201":{"description":"Interior assets updated"}},"security":[{"bearer":[]}],"summary":"Upload/replace interior design assets by section","tags":["Admin"]},"get":{"operationId":"AdminPublicController_listInteriorAssets","parameters":[],"responses":{"200":{"description":"Interior assets fetched"}},"summary":"List interior design asset IDs by section","tags":["Admin"]}},"/api/v1/admin/property/{id}/review":{"post":{"operationId":"AdminController_reviewProperty","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovePropertyDto"}}}},"responses":{"200":{"description":"Property reviewed successfully"},"400":{"description":"Bad request - invalid status or missing rejection note"},"401":{"description":"Unauthorized - admin access required"},"404":{"description":"Property not found"}},"security":[{"bearer":[]}],"summary":"Review a property (approve or reject)","tags":["Admin"]}},"/api/v1/admin/property/{id}/feature":{"post":{"operationId":"AdminController_featureProperty","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Property marked as featured successfully"},"400":{"description":"Bad request - only approved properties can be featured"},"401":{"description":"Unauthorized - admin access required"},"404":{"description":"Property not found"}},"security":[{"bearer":[]}],"summary":"Mark a property as featured","tags":["Admin"]}},"/api/v1/admin/property/{id}/transfer":{"patch":{"operationId":"AdminController_transferProperty","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferPropertyDto"}}}},"responses":{"200":{"description":"Property transferred successfully"},"400":{"description":"Bad request - invalid agent provided"},"401":{"description":"Unauthorized - admin access required"},"404":{"description":"Property or agent not found"}},"security":[{"bearer":[]}],"summary":"Transfer property ownership to another agent","tags":["Admin"]}},"/api/v1/admin/property/{id}/unfeature":{"post":{"operationId":"AdminController_unfeatureProperty","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Property unfeatured successfully"},"401":{"description":"Unauthorized - admin access required"},"404":{"description":"Property not found"}},"security":[{"bearer":[]}],"summary":"Remove featured status from a property","tags":["Admin"]}},"/api/v1/admin/property/pending":{"get":{"operationId":"AdminController_getPendingProperties","parameters":[],"responses":{"200":{"description":"List of pending properties retrieved successfully"},"401":{"description":"Unauthorized - admin access required"}},"security":[{"bearer":[]}],"summary":"Get all properties pending approval","tags":["Admin"]}},"/api/v1/admin/property/history":{"get":{"operationId":"AdminController_getPropertyHistory","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Property review history retrieved successfully"},"401":{"description":"Unauthorized - admin access required"}},"security":[{"bearer":[]}],"summary":"Get property review history","tags":["Admin"]}},"/api/v1/admin/property/stats":{"get":{"operationId":"AdminController_getPropertyStats","parameters":[],"responses":{"200":{"description":"Property statistics retrieved successfully"},"401":{"description":"Unauthorized - admin access required"}},"security":[{"bearer":[]}],"summary":"Get property statistics for admin dashboard","tags":["Admin"]}},"/api/v1/admin/product/stats":{"get":{"operationId":"AdminController_getProductStats","parameters":[],"responses":{"200":{"description":"Product statistics retrieved successfully"},"401":{"description":"Unauthorized - admin access required"}},"security":[{"bearer":[]}],"summary":"Get product statistics for admin dashboard","tags":["Admin"]}},"/api/v1/admin/customers":{"get":{"operationId":"AdminController_getCustomers","parameters":[],"responses":{"200":{"description":"Customers retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get customers with order counts","tags":["Admin"]}},"/api/v1/admin/customers/{id}":{"get":{"operationId":"AdminController_getCustomerById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Customer retrieved successfully"},"404":{"description":"Customer not found"}},"security":[{"bearer":[]}],"summary":"Get customer by ID with order details","tags":["Admin"]},"delete":{"operationId":"AdminController_deleteCustomer","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Customer deleted successfully"},"404":{"description":"Customer not found"}},"security":[{"bearer":[]}],"summary":"Delete customer by ID","tags":["Admin"]}},"/api/v1/admin/agents":{"get":{"operationId":"AdminController_getAgents","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Agents retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get agents list with optional status filter","tags":["Admin"]}},"/api/v1/admin/agents/{id}":{"get":{"operationId":"AdminController_getAgentById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Agent retrieved successfully"},"404":{"description":"Agent not found"}},"security":[{"bearer":[]}],"summary":"Get agent by ID","tags":["Admin"]},"patch":{"operationId":"AdminController_updateAgent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentDto"}}}},"responses":{"200":{"description":"Agent updated successfully"},"404":{"description":"Agent not found"}},"security":[{"bearer":[]}],"summary":"Update agent by ID","tags":["Admin"]},"delete":{"operationId":"AdminController_deleteAgent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"Agent deleted successfully"},"400":{"description":"Cannot delete agent with active properties"},"404":{"description":"Agent not found"}},"security":[{"bearer":[]}],"summary":"Delete agent by ID","tags":["Admin"]}},"/api/v1/admin/contact":{"post":{"operationId":"AdminPublicController_contactAdmin","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactAdminDto"}}}},"responses":{"201":{"description":"Message submitted successfully","content":{"application/json":{"schema":{"example":{"message":"Thanks! We have received your message and will get back to you shortly."}}}}}},"summary":"Send a contact message to the admin team","tags":["Admin"]}},"/api/v1/appointments/inspections":{"post":{"operationId":"AppointmentsController_createInspection","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInspectionDto"}}}},"responses":{"201":{"description":"Inspection created successfully"},"404":{"description":"Property not found"}},"security":[{"bearer":[]}],"summary":"Create property inspection request","tags":["Appointments"]},"get":{"operationId":"AppointmentsController_findAllInspections","parameters":[{"name":"status","required":false,"in":"query","description":"Filter by appointment status","schema":{"enum":["pending","scheduled","confirmed","completed","cancelled"],"type":"string"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"number"}},{"name":"agentId","required":false,"in":"query","description":"Filter by agent ID","schema":{"type":"number"}}],"responses":{"200":{"description":"List of inspections"}},"security":[{"bearer":[]}],"summary":"Get all inspections","tags":["Appointments"]}},"/api/v1/appointments/inspections/my-inspections":{"get":{"operationId":"AppointmentsController_findMyInspections","parameters":[],"responses":{"200":{"description":"User inspections retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get all inspections for the authenticated user","tags":["Appointments"]}},"/api/v1/appointments/inspections/{id}":{"get":{"operationId":"AppointmentsController_findOneInspection","parameters":[{"name":"id","required":true,"in":"path","description":"Inspection ID","schema":{"type":"number"}}],"responses":{"200":{"description":"Inspection details"},"404":{"description":"Inspection not found"}},"security":[{"bearer":[]}],"summary":"Get single inspection by ID","tags":["Appointments"]},"patch":{"operationId":"AppointmentsController_updateInspection","parameters":[{"name":"id","required":true,"in":"path","description":"Inspection ID","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAppointmentDto"}}}},"responses":{"200":{"description":"Inspection updated successfully"},"404":{"description":"Inspection not found"}},"security":[{"bearer":[]}],"summary":"Update inspection status or details","tags":["Appointments"]},"delete":{"operationId":"AppointmentsController_deleteInspection","parameters":[{"name":"id","required":true,"in":"path","description":"Inspection ID","schema":{"type":"number"}}],"responses":{"200":{"description":"Inspection deleted successfully"},"404":{"description":"Inspection not found"}},"security":[{"bearer":[]}],"summary":"Delete inspection","tags":["Appointments"]}},"/api/v1/appointments/consultations":{"post":{"operationId":"AppointmentsController_createConsultation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConsultationDto"}}}},"responses":{"201":{"description":"Consultation created successfully"}},"security":[{"bearer":[]}],"summary":"Create consultation request","tags":["Appointments"]},"get":{"operationId":"AppointmentsController_findAllConsultations","parameters":[{"name":"status","required":false,"in":"query","description":"Filter by appointment status","schema":{"enum":["pending","scheduled","confirmed","completed","cancelled"],"type":"string"}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"number"}}],"responses":{"200":{"description":"List of consultations"}},"security":[{"bearer":[]}],"summary":"Get all consultations","tags":["Appointments"]}},"/api/v1/appointments/consultations/my-consultations":{"get":{"operationId":"AppointmentsController_findMyConsultations","parameters":[],"responses":{"200":{"description":"User consultations retrieved successfully"}},"security":[{"bearer":[]}],"summary":"Get all consultations for the authenticated user","tags":["Appointments"]}},"/api/v1/appointments/consultations/{id}":{"get":{"operationId":"AppointmentsController_findOneConsultation","parameters":[{"name":"id","required":true,"in":"path","description":"Consultation ID","schema":{"type":"number"}}],"responses":{"200":{"description":"Consultation details"},"404":{"description":"Consultation not found"}},"security":[{"bearer":[]}],"summary":"Get single consultation by ID","tags":["Appointments"]},"patch":{"operationId":"AppointmentsController_updateConsultation","parameters":[{"name":"id","required":true,"in":"path","description":"Consultation ID","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAppointmentDto"}}}},"responses":{"200":{"description":"Consultation updated successfully"},"404":{"description":"Consultation not found"}},"security":[{"bearer":[]}],"summary":"Update consultation status or details","tags":["Appointments"]},"delete":{"operationId":"AppointmentsController_deleteConsultation","parameters":[{"name":"id","required":true,"in":"path","description":"Consultation ID","schema":{"type":"number"}}],"responses":{"200":{"description":"Consultation deleted successfully"},"404":{"description":"Consultation not found"}},"security":[{"bearer":[]}],"summary":"Delete consultation","tags":["Appointments"]}},"/api/v1/appointments/consultations/{id}/cancel":{"patch":{"operationId":"AppointmentsController_cancelConsultation","parameters":[{"name":"id","required":true,"in":"path","description":"Consultation ID","schema":{"type":"number"}}],"responses":{"200":{"description":"Consultation cancelled successfully"}},"security":[{"bearer":[]}],"summary":"Cancel a consultation and process refund if applicable","tags":["Appointments"]}},"/api/v1/appointments/agents/{agentId}/inspections":{"get":{"operationId":"AppointmentsController_findAgentInspections","parameters":[{"name":"agentId","required":true,"in":"path","description":"Agent ID","schema":{"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"enum":["pending","scheduled","confirmed","completed","cancelled"],"type":"string"}}],"responses":{"200":{"description":"List of inspections for the agent"}},"security":[{"bearer":[]}],"summary":"Get inspections assigned to an agent","tags":["Appointments"]}},"/api/v1/appointments/agents/{agentId}/consultations":{"get":{"operationId":"AppointmentsController_findAgentConsultations","parameters":[{"name":"agentId","required":true,"in":"path","description":"Agent ID","schema":{"type":"number"}},{"name":"status","required":false,"in":"query","schema":{"enum":["pending","scheduled","confirmed","completed","cancelled"],"type":"string"}}],"responses":{"200":{"description":"List of consultations for the agent"}},"security":[{"bearer":[]}],"summary":"Get consultations assigned to an agent","tags":["Appointments"]}},"/api/v1/voice-search/properties":{"post":{"operationId":"VoiceSearchController_searchProperties","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"audio":{"type":"string","format":"binary","description":"Recorded audio file (WebM, MP3, MPEG-4 audio)"},"query":{"type":"string","description":"Optional raw text query. When provided, audio becomes optional."},"limit":{"type":"number","default":10,"description":"Maximum number of results to return"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceSearchResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Search properties using a voice recording","tags":["Voice Search"]}}},"info":{"title":"Breedgestone API","description":"Breedgestone Property Management & Marketplace API Documentation","version":"1.0","contact":{}},"tags":[{"name":"Auth","description":"Authentication endpoints (Login, Register, OAuth, Password Reset)"},{"name":"Users","description":"User management endpoints"},{"name":"Notifications","description":"Notification management endpoints"}],"servers":[{"url":"http://localhost:10000","description":"Local Development"},{"url":"https://breedgestone-backend.onrender.com","description":"Staging Server (Render)"},{"url":"https://staging.breedgestone.com","description":"Staging Server"},{"url":"https://api.breedgestone.com","description":"Production Server"}],"components":{"securitySchemes":{"accessToken":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"Authorization","description":"Enter JWT token","in":"header"}},"schemas":{"PropertySettingsDto":{"type":"object","properties":{"minStayDuration":{"type":"string","example":"1 day"},"maxStayDuration":{"type":"string","example":"3 months"},"allowAdvanceBooking":{"type":"boolean","example":true},"bookingNoticePeriod":{"type":"string","example":"3 days"},"rentFrequency":{"type":"string","enum":["daily","weekly","monthly","quarterly","yearly"],"example":"monthly"},"renewalOption":{"type":"boolean","example":true}}},"CreatePropertyFeeDto":{"type":"object","properties":{"feeCategory":{"type":"string","description":"Fee category (e.g., \"Additional Fees (One-time, upfront)\", \"Recurring Fees (Monthly)\")","example":"Additional Fees (One-time, upfront)"},"feeName":{"type":"string","description":"Name of the fee","example":"Caution Deposit"},"purpose":{"type":"string","description":"Purpose or description of the fee","example":"Security for Property Damages"},"price":{"type":"number","description":"Fee amount in Naira","example":500000}},"required":["feeCategory","feeName","purpose","price"]},"CreatePropertyDto":{"type":"object","properties":{"agentId":{"type":"number","description":"Agent ID to assign the property to (Admin only - if not provided, defaults to the authenticated user)","example":5},"title":{"type":"string","description":"Property title","example":"Luxury 3 Bedroom Duplex"},"description":{"type":"string","description":"Detailed description of the property","example":"Spacious duplex with modern finishing, located in a secure estate."},"location":{"type":"string","description":"Location (free text, e.g. \"Jabi, Abuja\")","example":"Jabi, Abuja"},"address":{"type":"string","description":"Full address of the property","example":"15 Admiralty Way, Lekki Phase 1"},"type":{"type":"string","description":"Property type","enum":["house","apartment","land","commercial","office"],"example":"apartment"},"amount":{"type":"number","description":"Amount in Naira","example":25000000},"priceLabel":{"type":"string","description":"Price label for display","enum":["per_day","per_month","per_year","total"],"example":"total"},"category":{"type":"string","description":"Listing category (rental, sale, or shortlet)","enum":["rental","sale","shortlet"],"example":"rental"},"propertySize":{"type":"string","description":"Property size category","enum":["self_contained","studio","1_bedroom","2_bedroom","3_bedroom","4_bedroom","5_bedroom","duplex","bungalow","semi_detached","terraced","detached"],"example":"3_bedroom"},"availabilityStatus":{"type":"string","description":"Availability status of the property","enum":["available","occupied","sold","upcoming"],"example":"available"},"furnishing":{"type":"string","description":"Furnishing type","example":"unfurnished","enum":["furnished","semi_furnished","unfurnished"]},"amenities":{"description":"Array of amenities","example":["Swimming Pool","Gym","Security","24/7 Power"],"type":"array","items":{"type":"string"}},"bathroomCount":{"type":"number","description":"Total number of bathrooms in the property","example":3},"sizeInMeasurement":{"type":"string","description":"Free-form size measurement (e.g. \"250 sqm\")","example":"250 sqm"},"settings":{"description":"Additional property settings metadata","allOf":[{"$ref":"#/components/schemas/PropertySettingsDto"}]},"fees":{"description":"Property fees (e.g., caution deposit, legal fees, service charges)","example":[{"feeCategory":"Additional Fees (One-time, upfront)","feeName":"Caution Deposit","purpose":"Security for Property Damages","price":500000},{"feeCategory":"Recurring Fees (Monthly)","feeName":"Service Charge","purpose":"Estate Maintenance and Security","price":50000}],"type":"array","items":{"$ref":"#/components/schemas/CreatePropertyFeeDto"}},"coverAssetId":{"type":"number","description":"Asset ID for the main cover photo (uploaded via /upload endpoint)","example":69},"galleryAssetIds":{"description":"Array of asset IDs for gallery photos (uploaded via /upload endpoint)","example":[70],"type":"array","items":{"type":"number"}},"videoAssetIds":{"description":"Array of asset IDs for videos (uploaded via /upload endpoint)","example":[71],"type":"array","items":{"type":"number"}},"agreementTemplateAssetId":{"type":"number","description":"Asset ID for the property agreement template PDF (for rental and shortlet properties)","example":100}},"required":["title","description","location","address","type","amount","category","propertySize","furnishing","amenities","fees","coverAssetId","galleryAssetIds","videoAssetIds"]},"Property":{"type":"object","properties":{}},"UpdatePropertyDto":{"type":"object","properties":{"agentId":{"type":"number","description":"Agent ID to assign the property to (Admin only - if not provided, defaults to the authenticated user)","example":5},"title":{"type":"string","description":"Property title","example":"Luxury 3 Bedroom Duplex"},"description":{"type":"string","description":"Detailed description of the property","example":"Spacious duplex with modern finishing, located in a secure estate."},"location":{"type":"string","description":"Location (free text, e.g. \"Jabi, Abuja\")","example":"Jabi, Abuja"},"address":{"type":"string","description":"Full address of the property","example":"15 Admiralty Way, Lekki Phase 1"},"type":{"type":"string","description":"Property type","enum":["house","apartment","land","commercial","office"],"example":"apartment"},"amount":{"type":"number","description":"Amount in Naira","example":25000000},"priceLabel":{"type":"string","description":"Price label for display","enum":["per_day","per_month","per_year","total"],"example":"total"},"category":{"type":"string","description":"Listing category (rental, sale, or shortlet)","enum":["rental","sale","shortlet"],"example":"rental"},"propertySize":{"type":"string","description":"Property size category","enum":["self_contained","studio","1_bedroom","2_bedroom","3_bedroom","4_bedroom","5_bedroom","duplex","bungalow","semi_detached","terraced","detached"],"example":"3_bedroom"},"availabilityStatus":{"type":"string","description":"Availability status of the property","enum":["available","occupied","sold","upcoming"],"example":"available"},"furnishing":{"type":"string","description":"Furnishing type","example":"unfurnished","enum":["furnished","semi_furnished","unfurnished"]},"amenities":{"description":"Array of amenities","example":["Swimming Pool","Gym","Security","24/7 Power"],"type":"array","items":{"type":"string"}},"bathroomCount":{"type":"number","description":"Total number of bathrooms in the property","example":3},"sizeInMeasurement":{"type":"string","description":"Free-form size measurement (e.g. \"250 sqm\")","example":"250 sqm"},"settings":{"description":"Additional property settings metadata","allOf":[{"$ref":"#/components/schemas/PropertySettingsDto"}]},"fees":{"description":"Property fees (e.g., caution deposit, legal fees, service charges)","example":[{"feeCategory":"Additional Fees (One-time, upfront)","feeName":"Caution Deposit","purpose":"Security for Property Damages","price":500000},{"feeCategory":"Recurring Fees (Monthly)","feeName":"Service Charge","purpose":"Estate Maintenance and Security","price":50000}],"type":"array","items":{"$ref":"#/components/schemas/CreatePropertyFeeDto"}},"coverAssetId":{"type":"number","description":"Asset ID for the main cover photo (uploaded via /upload endpoint)","example":69},"galleryAssetIds":{"description":"Array of asset IDs for gallery photos (uploaded via /upload endpoint)","example":[70],"type":"array","items":{"type":"number"}},"videoAssetIds":{"description":"Array of asset IDs for videos (uploaded via /upload endpoint)","example":[71],"type":"array","items":{"type":"number"}},"agreementTemplateAssetId":{"type":"number","description":"Asset ID for the property agreement template PDF (for rental and shortlet properties)","example":100}}},"SignPropertyAgreementDto":{"type":"object","properties":{"assetId":{"type":"number","description":"Asset ID representing the buyer signature image","example":123},"acceptedTerms":{"type":"boolean","description":"Indicates that the buyer has accepted the terms in the agreement","example":true}},"required":["assetId","acceptedTerms"]},"CreatePropertyBookingDto":{"type":"object","properties":{"startDate":{"type":"string","description":"Booking start date (ISO string)","example":"2025-12-01T14:00:00Z"},"endDate":{"type":"string","description":"Booking end date (ISO string)","example":"2025-12-05T10:00:00Z"},"isSale":{"type":"boolean","description":"Mark this booking as a sale transaction","example":true}}},"FileMetadataDto":{"type":"object","properties":{"fileName":{"type":"string","description":"Original filename","example":"house-photo.jpg"},"fileType":{"type":"string","description":"MIME type of the file","example":"image/jpeg","enum":["image/jpeg","image/jpg","image/png","video/mp4"]},"fileSize":{"type":"number","description":"File size in bytes (max 10MB for images, 1000MB for videos)","example":2048000}},"required":["fileName","fileType","fileSize"]},"RequestPresignedUrlsDto":{"type":"object","properties":{"files":{"description":"Array of files metadata to request upload URLs for","example":[{"fileName":"house-photo.jpg","fileType":"image/jpeg","fileSize":2048000},{"fileName":"property-video.mp4","fileType":"video/mp4","fileSize":50000000}],"type":"array","items":{"$ref":"#/components/schemas/FileMetadataDto"}}},"required":["files"]},"InitializeEntityPaymentDto":{"type":"object","properties":{"type":{"type":"string","description":"Entity type for the payment","enum":["INSPECTION","CONSULTATION","ORDER","PROPERTY"],"example":"ORDER"},"entityId":{"type":"number","description":"Identifier of the entity (order ID, inspection ID, etc.)","example":123},"provider":{"type":"string","description":"Preferred payment provider","enum":["paystack","flutterwave"]}},"required":["type","entityId"]},"CreateUserDto":{"type":"object","properties":{"firstName":{"type":"string","example":"John","description":"User first name","maxLength":100},"lastName":{"type":"string","example":"Doe","description":"User last name","maxLength":100},"email":{"type":"string","example":"john.doe@example.com","description":"User email address"},"password":{"type":"string","example":"Password123!","description":"User password (min 8 characters, must contain uppercase, lowercase, and number/special character)","minLength":8},"phone":{"type":"string","example":"+2348012345678","description":"User phone number (international format)"},"profilePictureAssetId":{"type":"number","example":123,"description":"Uploaded asset ID to use as profile picture"},"gender":{"type":"string","example":"male","description":"User gender"},"dateOfBirth":{"type":"string","example":"1990-01-15","description":"Date of birth (ISO 8601 format)"},"bio":{"type":"string","example":"Software developer passionate about technology","description":"User biography","maxLength":1000},"role":{"type":"string","example":"user","description":"User role","enum":["admin","agent","user"],"default":"user"},"address":{"type":"string","example":"123 Main Street","description":"Street address","maxLength":255},"city":{"type":"string","example":"Lagos","description":"City","maxLength":100},"state":{"type":"string","example":"Lagos State","description":"State/Province","maxLength":100},"country":{"type":"string","example":"Nigeria","description":"Country","maxLength":100},"acceptTerms":{"type":"boolean","example":true,"description":"Accept terms and conditions"},"guestId":{"type":"number","example":1234567890,"description":"Temporary guest identifier returned by /users/guests for merging guest data on signup"}},"required":["firstName","lastName","email","password","acceptTerms"]},"UpdateUserDto":{"type":"object","properties":{"status":{"type":"string","example":"active","description":"User account status (admin only)","enum":["active","inactive","suspended","pending_verification","banned"]},"licenseNumber":{"type":"string","example":"LIC12345","description":"Professional license number","maxLength":100},"licenseExpiry":{"type":"string","example":"2025-12-31","description":"License expiry date"},"specializations":{"example":["Real Estate","Property Management"],"description":"Areas of specialization","type":"array","items":{"type":"string"}},"yearsOfExperience":{"type":"number","example":5,"description":"Years of professional experience","minimum":0,"maximum":100},"isVendor":{"type":"boolean","example":true,"description":"Whether user is a vendor"},"vendorDescription":{"type":"string","example":"We provide high-quality construction materials","description":"Vendor business description","maxLength":2000},"fcmToken":{"type":"string","example":"fcm_token_here","description":"Firebase Cloud Messaging token for push notifications"}}},"UpsertBillingProfileDto":{"type":"object","properties":{"address":{"type":"string","description":"Billing street address","maxLength":255},"state":{"type":"string","description":"State or region","maxLength":100},"country":{"type":"string","description":"Country","maxLength":100},"postalCode":{"type":"string","description":"Postal or ZIP code","maxLength":20}},"required":["address","state","country","postalCode"]},"SignupRequestDto":{"type":"object","properties":{"email":{"type":"string","example":"user@example.com","description":"User email address to receive OTP"}},"required":["email"]},"LoginUserDto":{"type":"object","properties":{"email":{"type":"string","example":"john.doe@example.com","description":"User email address"},"password":{"type":"string","example":"Password123!","description":"User password","minLength":6},"guestId":{"type":"number","example":123456789,"description":"Guest ID to migrate cart data from guest to user account"}},"required":["email","password"]},"ForgotPasswordDto":{"type":"object","properties":{"email":{"type":"string","example":"john.doe@example.com","description":"Email address to send password reset link"}},"required":["email"]},"ResetPasswordDto":{"type":"object","properties":{"email":{"type":"string","example":"john.doe@example.com","description":"User email address"},"otp":{"type":"string","example":"123456","description":"OTP code sent to email"},"newPassword":{"type":"string","example":"NewPassword123!","description":"New password (min 8 characters)","minLength":8}},"required":["email","otp","newPassword"]},"ChangePasswordDto":{"type":"object","properties":{"currentPassword":{"type":"string","example":"OldPassword123!","description":"Current password"},"newPassword":{"type":"string","example":"NewPassword123!","description":"New password (min 8 characters)","minLength":8}},"required":["currentPassword","newPassword"]},"CreateCategoryDto":{"type":"object","properties":{"name":{"type":"string","example":"Building Materials","description":"Category name"},"slug":{"type":"string","example":"building-materials","description":"URL-friendly category slug"},"description":{"type":"string","example":"All types of building and construction materials","description":"Category description"},"imageId":{"type":"string","example":"cat_img_123","description":"Category image ID"},"status":{"type":"string","example":"active","description":"Category status ('active' or 'inactive')"}},"required":["name","slug"]},"UpdateCategoryDto":{"type":"object","properties":{"name":{"type":"string","example":"Building Materials","description":"Category name"},"slug":{"type":"string","example":"building-materials","description":"URL-friendly category slug"},"description":{"type":"string","example":"All types of building and construction materials","description":"Category description"},"imageId":{"type":"string","example":"cat_img_123","description":"Category image ID"},"status":{"type":"string","example":"active","description":"Category status ('active' or 'inactive')"}}},"CreateSubCategoryDto":{"type":"object","properties":{"name":{"type":"string","example":"Cement","description":"Sub-category name"},"slug":{"type":"string","example":"cement","description":"URL-friendly sub-category slug"},"description":{"type":"string","example":"Various types of cement for construction","description":"Sub-category description"},"categoryId":{"type":"number","example":1,"description":"Parent category ID"},"imageId":{"type":"string","example":"subcat_img_123","description":"Sub-category image ID"},"status":{"type":"number","example":1,"description":"Sub-category status (1=active, 0=inactive)"}},"required":["name","slug","categoryId"]},"UpdateSubCategoryDto":{"type":"object","properties":{"name":{"type":"string","example":"Cement","description":"Sub-category name"},"slug":{"type":"string","example":"cement","description":"URL-friendly sub-category slug"},"description":{"type":"string","example":"Various types of cement for construction","description":"Sub-category description"},"categoryId":{"type":"number","example":1,"description":"Parent category ID"},"imageId":{"type":"string","example":"subcat_img_123","description":"Sub-category image ID"},"status":{"type":"number","example":1,"description":"Sub-category status (1=active, 0=inactive)"}}},"CreateProductDto":{"type":"object","properties":{"subCategoryId":{"type":"number","example":1,"description":"Sub-category ID"},"name":{"type":"string","example":"Premium Cement","description":"Product name"},"description":{"type":"string","example":"High-quality cement for construction projects","description":"Product description"},"price":{"type":"number","example":5500,"description":"Product price"},"discountedPrice":{"type":"number","example":3800,"description":"Discounted price"},"priceUnit":{"type":"string","example":"per bag","description":"Price unit (e.g., per bag, per ton)"},"sku":{"type":"string","example":"PREM-CEMENT-001","description":"Stock Keeping Unit (SKU)"},"stock":{"type":"number","example":150,"description":"Stock quantity"},"weight":{"type":"number","example":50,"description":"Weight in kg"},"deliveryUnitPrice":{"type":"number","example":100,"description":"Delivery unit price (base price for delivery fee calculation)"},"width":{"type":"number","example":30,"description":"Width in cm"},"height":{"type":"number","example":40,"description":"Height in cm"},"length":{"type":"number","example":50,"description":"Length in cm"},"shippingClass":{"type":"string","example":"Standard Shipping","description":"Shipping class"},"estimatedShippingTime":{"type":"number","example":3,"description":"Estimated shipping time in days"},"freeShipping":{"type":"boolean","example":false,"description":"Free shipping available"},"cashOnDelivery":{"type":"boolean","example":true,"description":"Cash on delivery available"},"keyFeatures":{"type":"string","example":"High durability\nWeather resistant\nEasy to apply","description":"Key features (comma or newline separated)"},"specifications":{"type":"string","example":"Compressive strength: 32.5 MPa\nSetting time: 30-600 minutes","description":"Product specifications"},"attributes":{"type":"object","example":{"color":"Gray","material":"Portland Cement","grade":"43"},"description":"Product attributes (color, size, etc.)"},"status":{"type":"number","example":1,"description":"Product status (1=active, 0=inactive)"},"coverAssetId":{"type":"number","description":"Asset ID for the main cover photo (uploaded via /upload endpoint)","example":69},"galleryAssetIds":{"description":"Array of asset IDs for gallery photos (uploaded via /upload endpoint)","example":[70,71,72],"type":"array","items":{"type":"number"}},"videoAssetIds":{"description":"Array of asset IDs for videos (uploaded via /upload endpoint)","example":[73],"type":"array","items":{"type":"number"}}},"required":["subCategoryId","name","description","coverAssetId","galleryAssetIds"]},"UpdateProductDto":{"type":"object","properties":{"subCategoryId":{"type":"number","example":1,"description":"Sub-category ID"},"name":{"type":"string","example":"Premium Cement","description":"Product name"},"description":{"type":"string","example":"High-quality cement for construction projects","description":"Product description"},"price":{"type":"number","example":5500,"description":"Product price"},"discountedPrice":{"type":"number","example":3800,"description":"Discounted price"},"priceUnit":{"type":"string","example":"per bag","description":"Price unit (e.g., per bag, per ton)"},"sku":{"type":"string","example":"PREM-CEMENT-001","description":"Stock Keeping Unit (SKU)"},"stock":{"type":"number","example":150,"description":"Stock quantity"},"weight":{"type":"number","example":50,"description":"Weight in kg"},"deliveryUnitPrice":{"type":"number","example":100,"description":"Delivery unit price (base price for delivery fee calculation)"},"width":{"type":"number","example":30,"description":"Width in cm"},"height":{"type":"number","example":40,"description":"Height in cm"},"length":{"type":"number","example":50,"description":"Length in cm"},"shippingClass":{"type":"string","example":"Standard Shipping","description":"Shipping class"},"estimatedShippingTime":{"type":"number","example":3,"description":"Estimated shipping time in days"},"freeShipping":{"type":"boolean","example":false,"description":"Free shipping available"},"cashOnDelivery":{"type":"boolean","example":true,"description":"Cash on delivery available"},"keyFeatures":{"type":"string","example":"High durability\nWeather resistant\nEasy to apply","description":"Key features (comma or newline separated)"},"specifications":{"type":"string","example":"Compressive strength: 32.5 MPa\nSetting time: 30-600 minutes","description":"Product specifications"},"attributes":{"type":"object","example":{"color":"Gray","material":"Portland Cement","grade":"43"},"description":"Product attributes (color, size, etc.)"},"status":{"type":"number","example":1,"description":"Product status (1=active, 0=inactive)"},"coverAssetId":{"type":"number","description":"Asset ID for the main cover photo (uploaded via /upload endpoint)","example":69},"galleryAssetIds":{"description":"Array of asset IDs for gallery photos (uploaded via /upload endpoint)","example":[70,71,72],"type":"array","items":{"type":"number"}},"videoAssetIds":{"description":"Array of asset IDs for videos (uploaded via /upload endpoint)","example":[73],"type":"array","items":{"type":"number"}}}},"AddToCartDto":{"type":"object","properties":{"productId":{"type":"number","example":1,"description":"Product ID to add to cart"},"quantity":{"type":"number","example":2,"description":"Quantity of product"},"subCategoryId":{"type":"number","example":1,"description":"SubCategory ID (if applicable)"}},"required":["productId","quantity"]},"UpdateCartItemDto":{"type":"object","properties":{"quantity":{"type":"number","example":3,"description":"New quantity for cart item"}},"required":["quantity"]},"CheckoutDto":{"type":"object","properties":{"paymentMethod":{"type":"string","example":"cash_on_delivery","description":"Payment method (cash_on_delivery, paystack, flutterwave)","enum":["cash_on_delivery","paystack","flutterwave"]},"tax":{"type":"number","example":750,"description":"Tax amount"},"note":{"type":"string","example":"Please deliver before 5pm","description":"Additional notes for the order"}}},"UpdateOrderDto":{"type":"object","properties":{"status":{"type":"string","description":"Order status","enum":["pending","confirmed","shipped","delivered","cancelled"],"example":"shipped"}},"required":["status"]},"CreateReviewDto":{"type":"object","properties":{"reviewable_type":{"type":"string","description":"Type of entity being reviewed","enum":["product","property","agent"],"example":"product"},"reviewable_id":{"type":"number","description":"ID of the entity being reviewed","example":1},"rating":{"type":"number","description":"Rating from 1 to 5 stars","minimum":1,"maximum":5,"example":5},"comment":{"type":"string","description":"Review comment/feedback","example":"Great product! Highly recommended."}},"required":["reviewable_type","reviewable_id","rating"]},"UpdateReviewDto":{"type":"object","properties":{"rating":{"type":"number","description":"Updated rating from 1 to 5 stars","minimum":1,"maximum":5,"example":4},"comment":{"type":"string","description":"Updated review comment/feedback","example":"Updated review comment."}}},"CreateChatDto":{"type":"object","properties":{"recipientId":{"type":"number","example":2,"description":"The user ID to start chat with"},"subject":{"type":"string","example":"Product inquiry","description":"Optional chat subject"}},"required":["recipientId"]},"Chat":{"type":"object","properties":{}},"ChatMessage":{"type":"object","properties":{}},"UpdateChatDto":{"type":"object","properties":{"status":{"type":"string","example":"closed","description":"Chat status","enum":["active","closed"]}}},"CreateAgentDto":{"type":"object","properties":{"firstName":{"type":"string","description":"Agent first name","example":"John","minLength":2,"maxLength":100},"lastName":{"type":"string","description":"Agent last name","example":"Doe","minLength":2,"maxLength":100},"email":{"type":"string","description":"Agent email address","example":"agent@example.com"},"phone":{"type":"string","description":"Agent phone number","example":"+2348012345678"},"gender":{"type":"string","description":"Agent gender","example":"Male","enum":["Male","Female","Other"]},"country":{"type":"string","description":"Agent country","example":"Nigeria"},"state":{"type":"string","description":"Agent state/province","example":"Lagos"},"address":{"type":"string","description":"Agent full address","example":"123 Main Street, Apartment 4B"}},"required":["firstName","lastName","email","phone","gender","country","state","address"]},"UpdateInteriorAssetsDto":{"type":"object","properties":{"diningRoomAssetIds":{"example":[1,2,3],"description":"Dining room asset IDs","type":"array","items":{"type":"number"}},"bedroomAssetIds":{"example":[4,5],"description":"Bedroom asset IDs","type":"array","items":{"type":"number"}},"bathroomAssetIds":{"example":[6],"description":"Bathroom asset IDs","type":"array","items":{"type":"number"}},"officeAssetIds":{"example":[7],"description":"Office asset IDs","type":"array","items":{"type":"number"}},"kitchenAssetIds":{"example":[8],"description":"Kitchen asset IDs","type":"array","items":{"type":"number"}},"hallwayAssetIds":{"example":[9],"description":"Hallway asset IDs","type":"array","items":{"type":"number"}},"otherAssetIds":{"example":[10],"description":"Other asset IDs","type":"array","items":{"type":"number"}}}},"ApprovePropertyDto":{"type":"object","properties":{"action":{"type":"string","description":"Action to take on the property","example":"approve","enum":["approve","reject"]},"rejectionReason":{"type":"string","description":"Reason for rejection (required if action is reject)","example":"Property does not meet our quality standards"}},"required":["action"]},"TransferPropertyDto":{"type":"object","properties":{"agentId":{"type":"number","description":"Identifier of the agent who will take ownership of the property","example":42}},"required":["agentId"]},"UpdateAgentDto":{"type":"object","properties":{"firstName":{"type":"string","description":"Agent first name","example":"John","minLength":2,"maxLength":100},"lastName":{"type":"string","description":"Agent last name","example":"Doe","minLength":2,"maxLength":100},"email":{"type":"string","description":"Agent email address","example":"agent@example.com"},"phone":{"type":"string","description":"Agent phone number","example":"+2348012345678"},"gender":{"type":"string","description":"Agent gender","example":"Male","enum":["Male","Female","Other"]},"country":{"type":"string","description":"Agent country","example":"Nigeria"},"state":{"type":"string","description":"Agent state/province","example":"Lagos"},"address":{"type":"string","description":"Agent full address","example":"123 Main Street, Apartment 4B"}}},"ContactAdminDto":{"type":"object","properties":{"firstName":{"type":"string","example":"Jane","description":"Sender first name","maxLength":100},"lastName":{"type":"string","example":"Doe","description":"Sender last name","maxLength":100},"email":{"type":"string","example":"jane@example.com","description":"Contact email address"},"phone":{"type":"string","example":"+1 (555) 000-0000","description":"Contact phone number","maxLength":30},"reason":{"type":"string","example":"Inquiry about property listings","description":"Reason for contacting","maxLength":200},"message":{"type":"string","example":"I would love to schedule a viewing for the downtown apartment.","description":"Message body"},"agreedToTerms":{"type":"boolean","example":true,"description":"Indicates if the sender agreed to terms of use and privacy policy"}},"required":["firstName","lastName","email","message"]},"CreateInspectionDto":{"type":"object","properties":{"propertyId":{"type":"number","description":"Property ID to inspect","example":1},"firstName":{"type":"string","description":"First name","example":"John"},"lastName":{"type":"string","description":"Last name","example":"Doe"},"email":{"type":"string","description":"Email address","example":"john.doe@example.com"},"phone":{"type":"string","description":"Phone number","example":"+2348012345678"},"preferredDateTime":{"type":"string","description":"Preferred inspection date and time","example":"2025-10-15T10:00:00Z"},"message":{"type":"string","description":"Additional message or special requests","example":"Please call before arrival"},"amount":{"type":"number","description":"Inspection fee amount (ignored; fixed by server)","example":15000}},"required":["propertyId","firstName","lastName","email","phone","preferredDateTime"]},"UpdateAppointmentDto":{"type":"object","properties":{"status":{"type":"string","enum":["pending","scheduled","confirmed","completed","cancelled"],"description":"Appointment status","example":"confirmed"},"scheduledAt":{"format":"date-time","type":"string","description":"Scheduled appointment time","example":"2025-10-15T10:00:00Z"},"confirmedAt":{"format":"date-time","type":"string","description":"Confirmation timestamp","example":"2025-10-14T09:00:00Z"},"completedAt":{"format":"date-time","type":"string","description":"Completion timestamp","example":"2025-10-15T12:00:00Z"},"cancelledAt":{"format":"date-time","type":"string","description":"Cancellation timestamp","example":"2025-10-14T15:00:00Z"},"cancellationReason":{"type":"string","description":"Reason for cancellation","example":"Client requested rescheduling"},"agentId":{"type":"number","description":"Agent ID responsible for the appointment","example":12}}},"CreateConsultationDto":{"type":"object","properties":{"firstName":{"type":"string","description":"First name","example":"John"},"lastName":{"type":"string","description":"Last name","example":"Doe"},"email":{"type":"string","description":"Email address","example":"john.doe@example.com"},"phone":{"type":"string","description":"Phone number","example":"+2348012345678"},"preferredDateTime":{"type":"string","description":"Preferred consultation date and time","example":"2025-10-15T14:00:00Z"},"message":{"type":"string","description":"Consultation topic or questions","example":"Need advice on property investment"},"amount":{"type":"number","description":"Consultation fee amount (ignored; fixed by server)","example":30000}},"required":["firstName","lastName","email","phone","preferredDateTime"]},"FindPropertiesDto":{"type":"object","properties":{"page":{"type":"number","description":"Page number (pagination)"},"limit":{"type":"number","description":"Items per page (pagination)"},"sortBy":{"type":"string","description":"Sort directive, e.g., createdAt:DESC"},"status":{"type":"string","description":"Property status filter","enum":["draft","pending_approval","approved","rejected","inactive"],"example":"approved"},"type":{"type":"string","description":"Property type filter","example":"apartment"},"category":{"type":"string","description":"Listing category (rental, sale, or shortlet)","enum":["rental","sale","shortlet"],"example":"rental"},"location":{"type":"string","description":"Location filter","example":"Lekki Phase 1, Lagos"},"propertySize":{"type":"string","description":"Number of bedrooms (2, 3, 4, 5, or 6)","enum":["self_contained","studio","1_bedroom","2_bedroom","3_bedroom","4_bedroom","5_bedroom","duplex","bungalow","semi_detached","terraced","detached"],"example":"4_bedroom"},"furnishing":{"type":"string","description":"Furnishing type filter","enum":["furnished","semi_furnished","unfurnished"],"example":"furnished"},"minAmount":{"type":"number","description":"Minimum amount in Naira","example":1000000},"maxAmount":{"type":"number","description":"Maximum amount in Naira","example":100000000}}},"VoiceSearchResponseDto":{"type":"object","properties":{"transcript":{"type":"string","description":"Transcript generated from the uploaded voice request"},"filters":{"description":"Filters inferred from the voice transcript","allOf":[{"$ref":"#/components/schemas/FindPropertiesDto"}]},"results":{"type":"object","description":"Property search results"},"summary":{"type":"string","description":"Natural language summary of what was understood"}},"required":["transcript","results"]}}},"security":[{"accessToken":[]}]}