openapi: 3.0.0
info:
  title: UPowr Project API
  version: 1.0.0
servers:
  - url: /api/v1/projects
security:
  - session: []
paths:
  /{projectId}/install-preferences:
    post:
      summary: Update customer installation preferences
      description: Update customer installation preferences
      operationId: updateInstallPrefs
      security:
        - session:
            - project:install-preferences:update
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        description: >-
          Array of numbers representing days of the week (0=Sunday, 1=Monday,
          2=Tuesday, ..., 6=Saturday)
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                type: integer
                minimum: 0
                maximum: 6
      responses:
        '200':
          description: Successfully updated preferences
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                properties:
                  ok:
                    type: boolean
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          description: Error updating preferences
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnknownError'
  /{projectId}/contract/accept:
    post:
      summary: Accept the project contract
      description: Accept the project contract
      operationId: acceptContract
      security:
        - session:
            - project:contract:accept
      parameters:
        - $ref: '#/components/parameters/projectId'
        - in: query
          name: re_accept
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Successfully accepted contract
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                properties:
                  ok:
                    type: boolean
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          description: Error accepting contract
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnknownError'
  /:
    post:
      summary: Create a new project.
      description: Create a new project.
      operationId: createProject
      security:
        - session:
            - project:create
        - unsecured: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - productsOfInterest
              properties:
                type:
                  type: string
                  description: The type of project.
                  default: SALES
                  enum:
                    - SALES
                    - SERVICE
                fileAssociationId:
                  type: string
                  description: >-
                    The unique identifier to that associates files uploaded with
                    this project. This ID links the files previously uploaded
                    directly to the newly created project. The fileAssociationId
                    must match the fileAssociationId provided during the file
                    upload process to successfully associate the uploaded files
                    with this project.
                preferences:
                  $ref: '#/components/schemas/Preferences'
                productsOfInterest:
                  $ref: '#/components/schemas/ProductsOfInterest'
                customer:
                  $ref: '#/components/schemas/Customer'
                site:
                  $ref: '#/components/schemas/Site'
                customerComments:
                  type: string
                  description: Free text field to input any comments from the customer.
                customFields:
                  $ref: '#/components/schemas/CustomFields'
                customerId:
                  type: string
                  description: >-
                    The unique identifier for an existing customer. If provided,
                    the `customer` field must be undefined.
                  format: uuid
                  example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                siteId:
                  type: string
                  description: >-
                    The unique identifier for an existing site. Requires
                    customerId to be provided and the `site` field must be
                    undefined.
                  format: uuid
                  example: 4fa85f64-5717-4562-b3fc-2c963f66afa7
      responses:
        '201':
          $ref: '#/components/responses/ProjectCreated'
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /file:
    post:
      summary: Upload file to a temporary location.
      description: >-
        Upload a single file to a temporary location while the project is being
        created.
      operationId: uploadOnboardingFile
      parameters:
        - in: query
          name: replace
          schema:
            type: boolean
            default: false
      security:
        - session:
            - project:file:upload
        - unsecured: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - fileCode
                - file
                - fileAssociationId
              properties:
                fileAssociationId:
                  type: string
                  description: >-
                    A unique identifier for associating files that are uploaded.
                    This association ID ensures all files uploaded can be
                    grouped together. Clients should generate a unique
                    fileAssociationId for each batch of file uploads to maintain
                    clear association.
                file:
                  type: string
                  format: binary
                  description: File to be uploaded
                fileCode:
                  type: string
                  enum:
                    - switchboard_photo
                    - preferred_installation_location_photo
                    - site_access_photo
      responses:
        '200':
          $ref: '#/components/responses/FileUploaded'
        '400':
          $ref: '#/components/responses/FileUploadBadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /{projectId}/reject:
    post:
      summary: Reject the project
      description: Reject the project
      operationId: rejectProject
      security:
        - session:
            - project:reject
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        description: Project reject context
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - rejectReason
              properties:
                rejectReason:
                  type: string
                  description: The reason for rejecting the project.
      responses:
        '200':
          description: Successfully rejected project
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                properties:
                  ok:
                    type: boolean
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          description: Error rejecting project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnknownError'
  /{projectId}/close:
    post:
      summary: Close the project
      description: Close the project
      operationId: closeProject
      security:
        - session:
            - project:close
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        description: Project close context
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                closedBy:
                  type: string
                  description: The user who is closing the project.
                closedStateOverride:
                  type: string
                  deprecated: true
                  description: >-
                    The state to close the project with. DEPRECATED - prefer not
                    sending this and relying on backend logic to determine the
                    state.
                  enum:
                    - CANCELLED
                    - LOST
                  example: CANCELLED
      responses:
        '200':
          description: Successfully rejected project
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                properties:
                  ok:
                    type: boolean
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          description: Error rejecting project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnknownError'
components:
  parameters:
    projectId:
      name: projectId
      in: path
      description: The project ID
      required: true
      schema:
        type: string
        format: uuid
  schemas:
    UnknownError:
      type: object
      required:
        - message
      properties:
        message:
          type: string
    ProductsOfInterest:
      type: array
      description: >-
        This is a list of all the types of products that the customer is
        interested in purchasing.
      items:
        type: object
        properties:
          solar:
            type: boolean
            description: Indicates if the customer is interested in solar panels.
            example: false
          battery:
            type: boolean
            description: Indicates if the customer is interested in solar batteries.
            example: false
          ev_charger:
            type: boolean
            description: >-
              Indicates if the customer is interested in an electric vehicle
              charger.
            example: true
    Customer:
      type: object
      description: Object containing all fields associated to the customer.
      properties:
        firstName:
          type: string
          description: The customer's first name.
          minLength: 1
          maxLength: 40
          example: John
        lastName:
          type: string
          description: The customer's last name.
          minLength: 1
          maxLength: 40
          example: Doe
        email:
          type: string
          format: email
          description: The customer's email address.
          maxLength: 254
          example: john.doe@example.com
        mobile:
          type: string
          description: The customer's mobile phone number.
          pattern: ^(\+?61|0)4\d{8}$
          example: '0491570006'
        isVerifiedMobile:
          type: boolean
          description: >-
            This field indicates whether a Customer's mobile number has been
            verified as correct. It also controls whether you can send the
            Customer automated SMS notifications. By default, it is set to
            `false`, and automated SMS notifications are disabled. To send them,
            you must set it to `true`.
          default: false
        class:
          type: string
          description: The customer's class.
          pattern: ^[a-z0-9]+(?:_+[a-z0-9]+)*$
          example: example-class
        xref:
          type: string
          description: Field can be used to pass your own identifiers. ie. Lead Identifier.
          example: example-xref
        retailerXref:
          type: string
          description: >-
            Field can be used to pass your own identifiers. ie. Customer
            Identifier.
          example: example-retailer-xref
        isBusiness:
          type: boolean
          description: Indicates if the customer is a business customer.
          default: false
      required:
        - firstName
        - lastName
        - email
        - mobile
    CustomField:
      type: object
      description: Object containing name, description and value of custom field.
      properties:
        name:
          type: string
          description: Human-readable name of the custom field.
          example: example-field-name
        description:
          type: string
          description: Description of the custom field.
          example: Example field description
        type:
          type: string
          description: Data type of the custom field value.
          enum:
            - string
        value:
          type: string
          description: Value of the custom field.
          example: '8'
      required:
        - name
        - type
        - value
    CustomFields:
      type: object
      description: An object containing any number of custom fields.
      additionalProperties:
        $ref: '#/components/schemas/CustomField'
    ErrorResponse:
      type: object
      properties:
        statusCode:
          type: integer
          description: HTTP status code of error.
        errorCode:
          type: string
          description: Error code identifying type of error.
        message:
          type: string
          description: Message describing the error.
        errors:
          type: array
          description: Information about the specific errors.
          items:
            type: object
            properties:
              field:
                type: string
                description: The field associated with the error.
              message:
                type: string
                description: Message describing the error related to the field.
        path:
          type: string
          description: API endpoint used to cause the error.
        timestamp:
          type: string
          format: date-time
          description: Timestamp of when the error occurred.
      required:
        - statusCode
        - message
    FileUploadErrorResponse:
      type: object
      properties:
        statusCode:
          type: integer
          description: HTTP status code of error.
        errorCode:
          type: string
          description: Error code identifying type of error.
        message:
          type: string
          description: Message describing the error.
        errors:
          type: array
          description: Information about the specific errors.
          items:
            type: object
            properties:
              fileName:
                type: string
                description: The filename associated with the error.
              message:
                type: string
                description: Message describing the error related to the filename.
    FileUploadSuccessResponse:
      type: object
      properties:
        ok:
          type: boolean
          description: >-
            Indicates the outcome of the file upload operation. A value of true
            signifies that the file was successfully uploaded to the server,
            while false indicates that the upload process failed.
      required:
        - ok
    Preferences:
      type: object
      description: Object containing all fields associated with customer's preferences.
      properties:
        customerPreferredDays:
          type: array
          description: >-
            Array of numbers representing days of the week for installation,
            where Sunday = 0, Monday = 1, ..., Saturday = 6.
          items:
            type: integer
            minimum: 0
            maximum: 6
    Site:
      type: object
      description: Data specific to a site.
      properties:
        latitudeDeg:
          type: number
          description: Latitude in degrees.
          format: float
          minimum: -90
          maximum: 90
          example: 33.8688
        longitudeDeg:
          type: number
          description: Longitude in degrees.
          format: float
          minimum: -180
          maximum: 180
          example: 151.2093
        timezone:
          type: string
          description: >-
            The time zone of the location, specified in IANA Time Zone format
            (ie. Australa/Sydney).
          example: Australia/Sydney
        addressLine1:
          type: string
          description: The first line of the site's address.
          example: 300 Barangaroo Avenue
        addressLine2:
          type: string
          description: The second line of the site's address (optional).
          example: null
        suburb:
          type: string
          description: The suburb of the site.
          example: Barangaroo
        postCode:
          type: string
          description: The postcode of the site in Australian postcode format.
          pattern: ^\d{4}$
          example: '2000'
        state:
          type: string
          description: The state of the site.
          enum:
            - ACT
            - NSW
            - NT
            - QLD
            - SA
            - TAS
            - VIC
            - WA
          example: NSW
        phaseNum:
          type: number
          description: The electrical phase of the site (1 or 3). Leave empty if unknown.
          enum:
            - 1
            - 3
          example: 3
          nullable: true
        nmi:
          type: string
          description: National Meter Identifier (NMI).
          example: '41021234567'
        quarterlyBillAmount:
          type: number
          description: Quarterly bill amount entered by the customer.
        monthlyBillAmount:
          type: number
          description: Monthly bill amount entered by the customer.
      required:
        - addressLine1
        - suburb
        - postCode
      oneOf:
        - required:
            - quarterlyBillAmount
        - required:
            - monthlyBillAmount
        - not:
            anyOf:
              - required:
                  - quarterlyBillAmount
              - required:
                  - monthlyBillAmount
    SuccessResponse:
      type: object
      properties:
        id:
          type: string
          description: The unique identifier for the newly created project.
          format: uuid
      required:
        - id
  responses:
    ProjectCreated:
      description: Project created successfully.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SuccessResponse'
          examples:
            customerCreated:
              summary: Project created
              value:
                id: 3fa85f64-5717-4562-b3fc-2c963f66afa7
    BadRequestError:
      description: Bad request. Missing required fields or invalid data format.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missingField:
              summary: Missing required field
              value:
                statusCode: 400
                errorCode: MISSING_FIELD
                message: 'Missing required field: email'
                errors:
                  - field: email
                    message: The email field is required
    UnauthorizedError:
      description: Unauthorized access. Valid authentication credentials are required.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalidCredentials:
              summary: Invalid Credentials
              value:
                statusCode: 401
                errorCode: INVALID_CREDENTIALS
                message: Authentication failed due to invalid credentials
    InternalServerError:
      description: Internal Server Error. This indicates a server-side issue.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            unexpectedError:
              summary: Unexpected Error
              value:
                statusCode: 500
                errorCode: INTERNAL_SERVER_ERROR
                message: An unexpected error occurred
    FileUploaded:
      description: File uploaded successfully.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/FileUploadSuccessResponse'
          examples:
            uploadedFile:
              summary: Uploaded file
              value:
                ok: true
    FileUploadBadRequestError:
      description: >-
        The file upload request is invalid and cannot be processed due to
        incorrect request syntax.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/FileUploadErrorResponse'
          examples:
            incorrectType:
              summary: Incorrect file type
              value:
                statusCode: 400
                errorCode: INCORRECT_FILE_TYPE
                message: Incorrect file type
                errors:
                  - fileName: document.psd
                    message: The type provided is not supported
  securitySchemes:
    session:
      type: oauth2
      description: Machine-to-Machine Authorization
      flows:
        clientCredentials:
          tokenUrl: https://id.core.upowr.cloud/oauth/token
          scopes:
            project:install-preferences:update: Update customer installation preferences
            project:contract:accept: Accept contract
            project:file:upload: Upload files for a project
            project:create: Create a new project
            project:reject: Reject a project
    unsecured:
      type: apiKey
      in: cookie
      name: upowr.sid
      description: UPowr session cookie
