openapi: 3.0.3
info:
  title: Pavblock API
  version: 1.0.0
  description: OpenAPI documentation for the Pavblock / Neighborly backend.
servers:
  - url: http://localhost:3090
    description: Local server
  - url: /
    description: Current host
tags:
  - name: System
  - name: Auth
  - name: Services
  - name: Service Requests
  - name: Admin
  - name: Bookings
  - name: Ratings
  - name: Notifications
  - name: Payments
  - name: Wallet
  - name: Connect Account
  - name: Portfolio
  - name: User Extra Info
  - name: CTA Forms
  - name: QR Code
paths:
  /:
    get:
      tags: [System]
      summary: Health check
      responses:
        "200":
          description: Server is healthy.
          content:
            text/plain:
              schema:
                type: string
                example: Pavblock Server is healthy!
  /docs.json:
    get:
      tags: [System]
      summary: Fetch Swagger JSON
      responses:
        "200":
          description: Swagger specification.
  /api/v1/webhook:
    post:
      tags: [Payments]
      summary: Stripe webhook endpoint
      description: Receives raw Stripe webhook payloads. Requires the Stripe-Signature header.
      parameters:
        - in: header
          name: stripe-signature
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Webhook received.
        "400":
          $ref: "#/components/responses/BadRequest"

  /api/v1/auth/register:
    post:
      tags: [Auth]
      summary: Register a user
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/RegisterRequest"
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
        "422":
          $ref: "#/components/responses/ValidationError"
  /api/v1/auth/signup-init:
    post:
      tags: [Auth]
      summary: Start signup and send OTP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignupInitRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
        "422":
          $ref: "#/components/responses/ValidationError"
  /api/v1/auth/login:
    post:
      tags: [Auth]
      summary: Login user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /api/v1/auth/verify:
    post:
      tags: [Auth]
      summary: Verify OTP
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VerifyOtpRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /api/v1/auth/me:
    get:
      tags: [Auth]
      summary: Get current user profile
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /api/v1/auth/become-provider:
    post:
      tags: [Auth]
      summary: Service seeker submits provider profile and upgrades to both
      description: Saves UserExtraInfo and service location/radius, sets userType to both instantly. No Stripe validation on this endpoint.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - serviceName
                - description
                - serviceRadiusMiles
                - location_lng
                - location_lat
              properties:
                serviceName:
                  type: string
                description:
                  type: string
                serviceRadiusMiles:
                  type: number
                location_lng:
                  type: number
                location_lat:
                  type: number
                address:
                  type: string
                city:
                  type: string
                state:
                  type: string
                country:
                  type: string
                zip_code:
                  type: string
                yearsOfExperience:
                  type: number
                skills:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "400":
          $ref: "#/components/responses/BadRequest"
        "422":
          $ref: "#/components/responses/ValidationError"
  /api/v1/auth/switch-role:
    post:
      tags: [Auth]
      summary: Switch active role for a both-role account
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [activeRole]
              properties:
                activeRole:
                  type: string
                  enum: [seeker, provider]
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/forget-password:
    post:
      tags: [Auth]
      summary: Send password reset OTP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                  format: email
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/reset-password:
    post:
      tags: [Auth]
      summary: Reset password
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [password]
              properties:
                password:
                  type: string
                  format: password
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/change-password:
    put:
      tags: [Auth]
      summary: Change password
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [old_password, new_password]
              properties:
                old_password:
                  type: string
                  format: password
                new_password:
                  type: string
                  format: password
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/delete-account:
    delete:
      tags: [Auth]
      summary: Delete current account
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/logout:
    post:
      tags: [Auth]
      summary: Logout current user
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/notification-toggle:
    post:
      tags: [Auth]
      summary: Toggle notification preference
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/update-profile:
    post:
      tags: [Auth]
      summary: Update current user profile
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/ProfileRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/join-nearby-group:
    post:
      tags: [Auth]
      summary: Join nearby group
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                radiusInMiles:
                  type: number
                  default: 10
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/complete-signup:
    post:
      tags: [Auth]
      summary: Complete signup profile
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/CompleteSignupRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/auth/web-content:
    get:
      tags: [Auth]
      summary: Get public web content
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/service/create:
    post:
      tags: [Services]
      summary: Create service
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServiceRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/service/all:
    get:
      tags: [Services]
      summary: Get services
      parameters:
        - $ref: "#/components/parameters/CategoryQuery"
        - in: query
          name: pricing_type
          schema:
            type: string
            enum: [free, fixed, negotiable]
        - in: query
          name: availability
          schema:
            type: string
        - in: query
          name: isRequested
          schema:
            type: boolean
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/service/providers:
    get:
      tags: [Services]
      summary: Get service providers
      parameters:
        - in: query
          name: zip_code
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/service/single/{id}:
    get:
      tags: [Services]
      summary: Get service by ID
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "404":
          $ref: "#/components/responses/NotFound"
  /api/v1/service/update/{id}:
    put:
      tags: [Services]
      summary: Update service
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServiceRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/service/delete/{id}:
    delete:
      tags: [Services]
      summary: Delete service
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/service/providers-details/{id}:
    get:
      tags: [Services]
      summary: Get service provider details
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/service-request/create:
    post:
      tags: [Service Requests]
      summary: Create service request
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServiceRequestCreate"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/service-request:
    get:
      tags: [Service Requests]
      summary: Get service requests
      parameters:
        - in: query
          name: urgency_level
          schema:
            type: string
            enum: [low, medium, high]
        - $ref: "#/components/parameters/CategoryQuery"
        - in: query
          name: location
          schema:
            type: string
        - in: query
          name: created_by
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/service-request/{id}:
    get:
      tags: [Service Requests]
      summary: Get service request by ID
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
    put:
      tags: [Service Requests]
      summary: Update service request
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServiceRequestCreate"
      responses:
        "200":
          $ref: "#/components/responses/Success"
    delete:
      tags: [Service Requests]
      summary: Delete service request
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/service-request/update/status/{id}:
    put:
      tags: [Service Requests]
      summary: Update service request status
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        $ref: "#/components/requestBodies/StatusUpdate"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/admin/service/approved/{id}:
    post:
      tags: [Admin]
      summary: Approve service request
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/admin/service/create:
    post:
      tags: [Admin]
      summary: Create service category
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/Category"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/admin/category/create:
    post:
      tags: [Admin]
      summary: Create category
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/Category"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/admin/user/role-analytics:
    get:
      tags: [Admin]
      summary: Get user role analytics
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/admin/user/get-all-users:
    get:
      tags: [Admin]
      summary: Get all users
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: role
          schema:
            type: string
            enum: [seeker, provider, both, all]
          description: Filter by UI role label (maps to DB userType).
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/admin/user/download-users:
    get:
      tags: [Admin]
      summary: Download users spreadsheet
      parameters:
        - in: query
          name: password
          schema:
            type: string
      responses:
        "200":
          description: Excel file download.
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
  /api/v1/admin/service/get-all-services:
    get:
      tags: [Admin]
      summary: Get all services
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/admin/category/get-all-categories:
    get:
      tags: [Admin]
      summary: Get all categories
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/admin/category/delete/{id}:
    delete:
      tags: [Admin]
      summary: Delete category
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/admin/block-toggle/{id}:
    put:
      tags: [Admin]
      summary: Toggle user block status
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/booking/create:
    post:
      tags: [Bookings]
      summary: Create booking
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [serviceId, bookingDate]
              properties:
                serviceId:
                  type: string
                bookingDate:
                  type: string
                  format: date-time
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/booking/my-bookings:
    get:
      tags: [Bookings]
      summary: Get my bookings
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/booking/request:
    get:
      tags: [Bookings]
      summary: Get booking requests
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [pending, in_progress, completed, rejected]
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/booking/update-status/{bookingId}:
    post:
      tags: [Bookings]
      summary: Update booking status
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: bookingId
          required: true
          schema:
            type: string
      requestBody:
        $ref: "#/components/requestBodies/BookingStatusUpdate"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/booking/get-all-categories:
    get:
      tags: [Bookings]
      summary: Get all service categories
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/rating/create:
    post:
      tags: [Ratings]
      summary: Create rating
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RatingRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/rating:
    get:
      tags: [Ratings]
      summary: Get ratings
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: type
          schema:
            type: string
            enum: [given, received, all]
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/rating/update/{id}:
    put:
      tags: [Ratings]
      summary: Update rating
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RatingUpdateRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
    delete:
      tags: [Ratings]
      summary: Delete rating
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/notification:
    post:
      tags: [Notifications]
      summary: Create notification
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
    get:
      tags: [Notifications]
      summary: Fetch current user's notifications
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/notification/delete/{id}:
    delete:
      tags: [Notifications]
      summary: Delete notification
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/notification/update-status/{id}:
    put:
      tags: [Notifications]
      summary: Update notification read status
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                notificationToggle:
                  type: boolean
                deviceToken:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/notification/mark-all-as-read:
    put:
      tags: [Notifications]
      summary: Mark all notifications as read
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/payment/sent-payment:
    post:
      tags: [Payments]
      summary: Create a pending card payment transaction
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/PaymentIntent"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/payment/confirm-payment:
    post:
      tags: [Payments]
      summary: Transfer payment to provider
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/BookingId"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/payment/get-all-payment:
    get:
      tags: [Payments]
      summary: Get current user's payments
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/payment/get-transaction-history:
    get:
      tags: [Payments]
      summary: Get current user's transaction history
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/payment/payment-with-wallet:
    post:
      tags: [Payments]
      summary: Pay for booking using wallet balance
      security:
        - bearerAuth: []
      requestBody:
        $ref: "#/components/requestBodies/BookingId"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/wallet/connect-wallet:
    post:
      tags: [Wallet]
      summary: Connect wallet for current user
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/wallet/fetch-wallet:
    get:
      tags: [Wallet]
      summary: Fetch current user's wallet
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/wallet/wallet-top-up:
    post:
      tags: [Wallet]
      summary: Create wallet top-up payment intent
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [amount]
              properties:
                amount:
                  type: number
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/wallet/confirm-top-up:
    post:
      tags: [Wallet]
      summary: Confirm wallet top-up transaction
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [transactionId]
              properties:
                transactionId:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/connect-account/create-account:
    post:
      tags: [Connect Account]
      summary: Create Stripe connect account
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/connect-account/fetch-account:
    get:
      tags: [Connect Account]
      summary: Fetch current user's Stripe connect account
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/portfolio/create:
    post:
      tags: [Portfolio]
      summary: Create portfolio
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/PortfolioRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/portfolio/all:
    get:
      tags: [Portfolio]
      summary: Get portfolios
      parameters:
        - in: query
          name: user
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/portfolio/my-portfolios:
    get:
      tags: [Portfolio]
      summary: Get current user's portfolios
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/portfolio/{id}:
    get:
      tags: [Portfolio]
      summary: Get portfolio by ID
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/portfolio/update/{id}:
    put:
      tags: [Portfolio]
      summary: Update portfolio
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/PortfolioRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/portfolio/delete/{id}:
    delete:
      tags: [Portfolio]
      summary: Delete portfolio
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/user-extra-info/create-update:
    post:
      tags: [User Extra Info]
      summary: Create or update user extra information
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserExtraInfoRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/user-extra-info:
    get:
      tags: [User Extra Info]
      summary: Get current user's extra information
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/user-extra-info/{userId}:
    get:
      tags: [User Extra Info]
      summary: Get extra information for a user
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: userId
          required: true
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/user-extra-info/product/add:
    post:
      tags: [User Extra Info]
      summary: Add product to extra information
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/UserExtraItemRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/user-extra-info/product/update/{productId}:
    put:
      tags: [User Extra Info]
      summary: Update product
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: productId
          required: true
          schema:
            type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/UserExtraItemRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/user-extra-info/product/delete/{productId}:
    delete:
      tags: [User Extra Info]
      summary: Delete product
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: productId
          required: true
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/user-extra-info/service/add:
    post:
      tags: [User Extra Info]
      summary: Add service to extra information
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/UserExtraItemRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/user-extra-info/service/update/{serviceId}:
    put:
      tags: [User Extra Info]
      summary: Update extra information service
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: serviceId
          required: true
          schema:
            type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/UserExtraItemRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/user-extra-info/service/delete/{serviceId}:
    delete:
      tags: [User Extra Info]
      summary: Delete extra information service
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: serviceId
          required: true
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/cta-form/create:
    post:
      tags: [CTA Forms]
      summary: Create CTA form submission
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CTAFormRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/cta-form/all:
    get:
      tags: [CTA Forms]
      summary: Get all CTA forms
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/cta-form/download:
    get:
      tags: [CTA Forms]
      summary: Download CTA forms CSV
      responses:
        "200":
          description: CSV file download.
          content:
            text/csv:
              schema:
                type: string
  /api/v1/cta-form/{id}:
    get:
      tags: [CTA Forms]
      summary: Get CTA form by ID
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/cta-form/update/{id}:
    put:
      tags: [CTA Forms]
      summary: Update CTA form
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CTAFormRequest"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/cta-form/delete/{id}:
    delete:
      tags: [CTA Forms]
      summary: Delete CTA form
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"

  /api/v1/qrcode:
    post:
      tags: [QR Code]
      summary: Generate a QR code
      description: Destination URL is determined from the Referer header by the current controller.
      parameters:
        - in: header
          name: referer
          required: true
          schema:
            type: string
            format: uri
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/QRCodeGenerateRequest"
          application/json:
            schema:
              $ref: "#/components/schemas/QRCodeGenerateRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/qrcode/admin/generate:
    post:
      tags: [QR Code]
      summary: Admin generate QR code
      security:
        - bearerAuth: []
      parameters:
        - in: header
          name: referer
          required: true
          schema:
            type: string
            format: uri
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QRCodeGenerateRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/qrcode/admin:
    post:
      tags: [QR Code]
      summary: Legacy admin generate QR code with multipart upload
      security:
        - bearerAuth: []
      parameters:
        - in: header
          name: referer
          required: true
          schema:
            type: string
            format: uri
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/QRCodeGenerateRequest"
      responses:
        "201":
          $ref: "#/components/responses/Created"
  /api/v1/qrcode/r/{codeId}:
    get:
      tags: [QR Code]
      summary: Track scan and redirect
      parameters:
        - $ref: "#/components/parameters/CodeIdPath"
      responses:
        "301":
          description: Redirects to destination URL.
        "404":
          $ref: "#/components/responses/NotFound"
  /api/v1/qrcode/admin/all:
    get:
      tags: [QR Code]
      summary: Get all QR codes with scan counts
      security:
        - bearerAuth: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/qrcode/analytics/{codeId}:
    get:
      tags: [QR Code]
      summary: Get QR code analytics
      parameters:
        - $ref: "#/components/parameters/CodeIdPath"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /api/v1/qrcode/record-scan:
    get:
      tags: [QR Code]
      summary: Record QR scan from query parameters
      parameters:
        - in: query
          name: source
          schema:
            type: string
            example: qrcode
        - in: query
          name: codeId
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Success"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  parameters:
    IdPath:
      in: path
      name: id
      required: true
      schema:
        type: string
      description: MongoDB object ID.
    CodeIdPath:
      in: path
      name: codeId
      required: true
      schema:
        type: string
    CategoryQuery:
      in: query
      name: category
      schema:
        type: string
  requestBodies:
    Category:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [name]
            properties:
              name:
                type: string
              description:
                type: string
    StatusUpdate:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [status]
            properties:
              status:
                type: string
                enum: [pending, in_progress, completed]
    BookingStatusUpdate:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [status]
            properties:
              status:
                type: string
                enum: [pending, in_progress, completed, rejected]
    BookingId:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [booking_id]
            properties:
              booking_id:
                type: string
    PaymentIntent:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [booking_id, user_id, payment_intent_id]
            properties:
              booking_id:
                type: string
              user_id:
                type: string
              payment_intent_id:
                type: string
  responses:
    Success:
      description: Successful response.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiResponse"
    Created:
      description: Resource created.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiResponse"
    BadRequest:
      description: Bad request.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Unauthorized or invalid token.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    ValidationError:
      description: Validation error.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
  schemas:
    ApiResponse:
      type: object
      properties:
        success:
          type: boolean
        status:
          oneOf:
            - type: boolean
            - type: string
        message:
          type: string
        data:
          nullable: true
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        status:
          oneOf:
            - type: boolean
            - type: string
        message:
          type: string
        error:
          type: string
    Location:
      type: object
      properties:
        type:
          type: string
          enum: [Point]
          default: Point
        coordinates:
          type: array
          minItems: 2
          maxItems: 2
          items:
            type: number
          example: [-74.006, 40.7128]
    RegisterRequest:
      type: object
      required: [full_name, first_name, last_name, email, password, zip_code, address]
      properties:
        full_name:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        phone_number:
          type: string
        zip_code:
          type: string
        address:
          type: string
        city:
          type: string
        state:
          type: string
        country:
          type: string
        userType:
          oneOf:
            - type: integer
              enum: [1, 2]
            - type: string
              enum: [Service Seeker, Service Provider, "1", "2", user, service_provider]
        location:
          $ref: "#/components/schemas/Location"
        image:
          type: string
          format: binary
    SignupInitRequest:
      type: object
      required: [email, password, userType]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 6
        userType:
          type: string
          enum: [Service Seeker, Service Provider]
    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        fcmToken:
          type: string
        deviceToken:
          type: string
        location:
          $ref: "#/components/schemas/Location"
    VerifyOtpRequest:
      type: object
      required: [otp, otpType]
      properties:
        otp:
          type: string
        otpType:
          type: string
          enum: [forgetPassword, register]
    ProfileRequest:
      type: object
      required: [full_name, phone_number, country, first_name, zip_code, last_name]
      properties:
        full_name:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        gender:
          type: string
          enum: [male, female]
          nullable: true
        dob:
          type: string
        phone_number:
          type: string
        country:
          type: string
        state:
          type: string
          nullable: true
        city:
          type: string
          nullable: true
        zip_code:
          type: string
        image:
          type: string
          format: binary
    CompleteSignupRequest:
      type: object
      required: [userType, description]
      properties:
        userType:
          type: string
          enum: [Service Seeker, Service Provider, user, service_provider]
        serviceName:
          type: string
        description:
          type: string
        serviceRadiusMiles:
          oneOf:
            - type: number
            - type: string
        first_name:
          type: string
        last_name:
          type: string
        phone_number:
          type: string
        address:
          type: string
        zip_code:
          type: string
        country:
          type: string
        state:
          type: string
        city:
          type: string
        location_lng:
          oneOf:
            - type: number
            - type: string
        location_lat:
          oneOf:
            - type: number
            - type: string
        image:
          type: string
          format: binary
    ServiceRequest:
      type: object
      required: [service_title, description, pricing_type, location, service_category]
      properties:
        service_title:
          type: string
        description:
          type: string
        pricing_type:
          type: string
          enum: [free, fixed, negotiable]
        price:
          type: number
        location:
          type: string
        image:
          type: string
        service_category:
          type: string
        isRequested:
          type: boolean
    ServiceRequestCreate:
      type: object
      required: [service_title, description, urgency_level, location, category]
      properties:
        service_title:
          type: string
        description:
          type: string
        urgency_level:
          type: string
          enum: [low, medium, high]
        budget:
          type: number
        location:
          type: string
        availability:
          type: string
        category:
          type: string
    RatingRequest:
      type: object
      required: [bookingId, rating]
      properties:
        bookingId:
          type: string
        rating:
          type: number
          minimum: 1
          maximum: 5
        review:
          type: string
    RatingUpdateRequest:
      type: object
      required: [rating]
      properties:
        rating:
          type: number
          minimum: 1
          maximum: 5
        review:
          type: string
    NotificationRequest:
      type: object
      properties:
        type:
          type: string
        title:
          type: string
        messages:
          type: string
        link:
          type: string
        tokens:
          type: array
          items:
            type: string
    PortfolioRequest:
      type: object
      required: [title, description]
      properties:
        title:
          type: string
        description:
          type: string
        images:
          type: array
          items:
            type: string
            format: binary
    UserExtraInfoRequest:
      type: object
      properties:
        description:
          type: string
        yearsOfExperience:
          type: number
        products:
          type: array
          items:
            $ref: "#/components/schemas/UserExtraItem"
        services:
          type: array
          items:
            $ref: "#/components/schemas/UserExtraItem"
        skills:
          type: array
          items:
            type: string
        certifications:
          type: array
          items:
            type: object
        education:
          type: array
          items:
            type: object
        languages:
          type: array
          items:
            type: string
    UserExtraItem:
      type: object
      required: [name]
      properties:
        name:
          type: string
        description:
          type: string
        price:
          type: number
        category:
          type: string
        duration:
          type: string
        isAvailable:
          type: boolean
    UserExtraItemRequest:
      allOf:
        - $ref: "#/components/schemas/UserExtraItem"
        - type: object
          properties:
            image:
              type: string
              format: binary
    CTAFormRequest:
      type: object
      required: [full_name, business_email, phone_number, message, service]
      properties:
        full_name:
          type: string
        business_email:
          type: string
          format: email
        phone_number:
          type: string
        message:
          type: string
        service:
          type: string
    QRCodeGenerateRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        foreground:
          type: string
          example: "#0C1929"
        background:
          type: string
          example: "#FFFFFF"
        size:
          type: integer
          minimum: 200
          maximum: 1000
        logo:
          type: string
          format: binary
