openapi: 3.0.1
info:
  title: PCS neo in the Cloud - API
  version: V1.0.0
  description: >-
    Current API version is a work in progress, subject to changes.
    This file (swagger2.yml) is swagger.yml enriched with request/response
    schemas derived from invoke_pcsneo_api.py, Invoke-PcsNeoApi.ps1,
    bulk_provision_pcsneo.py, and Invoke-PcsNeoBulkProvision.ps1.
servers:
  - url: https://api.pcstest.del.public.siemens.com/{basePath}
    variables:
      basePath:
        default: ''
paths:
  /prepaid/tenants/{tenant_id}:
    delete:
      responses:
        default:
          description: Default response for DELETE /prepaid/tenants/{tenant_id}
      security:
        - siemensid-authorizer: []
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /pricingplans/{pricing_plan_id}:
    put:
      responses:
        '200':
          description: Updated pricing plan.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingPlan'
        default:
          description: Default response for PUT /pricingplans/{pricing_plan_id}
      security:
        - siemensid-authorizer: []
      summary: Update pricing plan
      description: Updates an existing pricing plan.
      operationId: updatePricingPlan
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PricingPlan'
        description: Pricing plan update payload.
        required: true
    delete:
      responses:
        '200':
          description: Pricing plan deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for DELETE /pricingplans/{pricing_plan_id}
      security:
        - siemensid-authorizer: []
      summary: Delete pricing plan
      description: Deletes an existing pricing plan by id.
      operationId: deletePricingPlan
    parameters:
      - name: pricing_plan_id
        in: path
        description: Generated path parameter for pricing_plan_id
        required: true
        schema:
          type: string
  /projects/{project_id}:
    put:
      responses:
        '200':
          description: Project updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for PUT /projects/{project_id}
      security:
        - siemensid-authorizer: []
      summary: Update project
      description: Updates project settings.
      operationId: updateProject
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectUpdateRequest'
        description: Project update payload.
        required: true
    delete:
      responses:
        '200':
          description: Project deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for DELETE /projects/{project_id}
      security:
        - siemensid-authorizer: []
      summary: Delete project
      description: Deletes a project.
      operationId: deleteProject
    parameters:
      - name: project_id
        in: path
        description: Generated path parameter for project_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/subtenants/{sub_tenant_id}:
    put:
      responses:
        '200':
          description: Subtenant updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: >-
            Default response for PUT
            /tenants/{tenant_id}/subtenants/{sub_tenant_id}
      security:
        - siemensid-authorizer: []
      summary: Update subtenant
      description: Updates subtenant pricing plan assignment.
      operationId: updateSubTenant
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubTenantUpdateRequest'
        description: Subtenant update payload.
        required: true
    delete:
      responses:
        '200':
          description: Subtenant deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: >-
            Default response for DELETE
            /tenants/{tenant_id}/subtenants/{sub_tenant_id}
      security:
        - siemensid-authorizer: []
      summary: Delete subtenant
      description: Deletes a subtenant.
      operationId: deleteSubTenant
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
      - name: sub_tenant_id
        in: path
        description: Generated path parameter for sub_tenant_id
        required: true
        schema:
          type: string
  /announcements:
    get:
      responses:
        '200':
          description: Announcements list.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Announcement'
                type: array
        default:
          description: Default response for GET /announcements
      security:
        - siemensid-authorizer: []
      summary: List announcements
      description: Returns all announcements visible to the user.
      operationId: getAnnouncements
  /announcements/images:
    get:
      responses:
        '200':
          description: Announcement image.
          content:
            image/*:
              schema:
                type: string
                format: binary
        default:
          description: Default response for GET /announcements/images
      security:
        - siemensid-authorizer: []
      summary: Get announcement image
      description: Returns announcement image as binary blob by image key.
      operationId: getAnnouncementImage
  /me:
    get:
      responses:
        '200':
          description: Current user profile.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthPortalUser'
        default:
          description: Default response for GET /me
      security:
        - siemensid-authorizer: []
      summary: Get current user
      description: Returns the authenticated portal user profile.
      operationId: getMe
  /myOrders:
    get:
      responses:
        default:
          description: Default response for GET /myOrders
      security:
        - siemensid-authorizer: []
  /parameterStoreFiles:
    get:
      responses:
        '200':
          description: Parameter store values.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ParameterStoreFilesResponse'
        default:
          description: Default response for GET /parameterStoreFiles
      security:
        - siemensid-authorizer: []
      summary: Get parameter store files
      description: Returns available PCS neo and SIMIT version values from parameter store.
      operationId: getAvailableNeoSimitVersions
  /projectcredits:
    get:
      responses:
        '200':
          description: Monthly credit export.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreditMonthlyExport'
        default:
          description: Default response for GET /projectcredits
      security:
        - siemensid-authorizer: []
      summary: Get tenant monthly credits
      description: >-
        Returns monthly tenant cost/credit export for the selected month and
        year.
      operationId: getTenantCosts
  /projects/{project_id}/simitFileUploadHistory:
    get:
      responses:
        '200':
          description: SIMIT upload history entries.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/SimitUploadHistoryItem'
                type: array
        default:
          description: >-
            Default response for GET
            /projects/{project_id}/simitFileUploadHistory
      security:
        - siemensid-authorizer: []
      summary: Get SIMIT upload history
      description: Returns historical SIMIT file uploads for a project.
      operationId: getSIMITFileUploadHistory
    parameters:
      - name: project_id
        in: path
        description: Generated path parameter for project_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/cover-image:
    get:
      responses:
        default:
          description: Default response for GET /tenants/{tenant_id}/cover-image
      security:
        - siemensid-authorizer: []
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/credit-limits:
    get:
      responses:
        default:
          description: Default response for GET /tenants/{tenant_id}/credit-limits
      security:
        - siemensid-authorizer: []
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/priceList:
    get:
      responses:
        '200':
          description: Tenant price list.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PriceListResponse'
        default:
          description: Default response for GET /tenants/{tenant_id}/priceList
      security:
        - siemensid-authorizer: []
      summary: Get tenant price list
      description: Returns tenant specific price list.
      operationId: getTenantPriceList
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/pricingplans:
    get:
      responses:
        '200':
          description: Pricing plans for tenant.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/PricingPlan'
                type: array
        default:
          description: Default response for GET /tenants/{tenant_id}/pricingplans
      security:
        - siemensid-authorizer: []
      summary: List pricing plans
      description: Returns all pricing plans for a tenant.
      operationId: getPricingPlans
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/projects:
    get:
      responses:
        '200':
          description: Tenant projects.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/ProjectExternal'
                type: array
        default:
          description: Default response for GET /tenants/{tenant_id}/projects
      security:
        - siemensid-authorizer: []
      summary: List tenant projects
      description: Returns projects belonging to a tenant.
      operationId: getTenantProjects
    post:
      responses:
        '200':
          description: Project created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for POST /tenants/{tenant_id}/projects
      security:
        - siemensid-authorizer: []
      summary: Create tenant project
      description: Creates a project for a tenant.
      operationId: createTenantProject
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreateRequest'
        description: Project creation payload.
        required: true
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/projects/{project_id}/instancesLogs:
    get:
      responses:
        '200':
          description: Project instance logs.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/ProjectInstanceLog'
                type: array
        default:
          description: >-
            Default response for GET
            /tenants/{tenant_id}/projects/{project_id}/instancesLogs
      security:
        - siemensid-authorizer: []
      summary: Get project instance logs
      description: Returns instance logs for selected date range.
      operationId: getInstancesLogs
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
      - name: project_id
        in: path
        description: Generated path parameter for project_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/subtenants:
    get:
      responses:
        '200':
          description: Subtenants for tenant.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/SubTenant'
                type: array
        default:
          description: Default response for GET /tenants/{tenant_id}/subtenants
      security:
        - siemensid-authorizer: []
      summary: List subtenants
      description: Returns all subtenants for a tenant.
      operationId: getSubTenants
    post:
      responses:
        '200':
          description: Created subtenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubTenant'
        default:
          description: Default response for POST /tenants/{tenant_id}/subtenants
      security:
        - siemensid-authorizer: []
      summary: Create subtenant
      description: Creates a subtenant under a tenant.
      operationId: createSubTenant
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubTenantCreateRequest'
        description: Subtenant creation payload.
        required: true
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/topups:
    get:
      responses:
        default:
          description: Default response for GET /tenants/{tenant_id}/topups
      security:
        - siemensid-authorizer: []
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/users:
    get:
      responses:
        '200':
          description: Tenant users.
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/TenantUser'
                type: array
        default:
          description: Default response for GET /tenants/{tenant_id}/users
      security:
        - siemensid-authorizer: []
      summary: List tenant users
      description: Returns users of a tenant.
      operationId: getTenantUsers
    post:
      responses:
        '200':
          description: Tenant user added.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for POST /tenants/{tenant_id}/users
      security:
        - siemensid-authorizer: []
      summary: Add tenant user
      description: Adds a user to a tenant.
      operationId: addTenantUser
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TenantUserAddRequest'
        description: Tenant user payload.
        required: true
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/users/{user_id}/removalInfo:
    get:
      responses:
        '200':
          description: User removal info.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TenantUserRemovalInfo'
        default:
          description: >-
            Default response for GET
            /tenants/{tenant_id}/users/{user_id}/removalInfo
      security:
        - siemensid-authorizer: []
      summary: Get user removal info
      description: Returns information whether a tenant user can be removed.
      operationId: getTenantUserRemovalInfo
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
      - name: user_id
        in: path
        description: Generated path parameter for user_id
        required: true
        schema:
          type: string
  /tenants/{tenant_id}/users/{user_id}:
    put:
      responses:
        '200':
          description: Tenant user updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for PUT /tenants/{tenant_id}/users/{user_id}
      security:
        - siemensid-authorizer: []
      summary: Update tenant user
      description: Updates role and display cost settings for a tenant user.
      operationId: updateTenantUser
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TenantUserUpdateRequest'
        description: Tenant user update payload.
        required: true
    patch:
      responses:
        '200':
          description: Tenant user removed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for PATCH /tenants/{tenant_id}/users/{user_id}
      security:
        - siemensid-authorizer: []
      summary: Remove tenant user
      description: Removes a user from a tenant. Send empty body or TenantUserRemoveRequest.
      operationId: removeTenantUser
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TenantUserRemoveRequest'
        description: Tenant user removal payload (may be empty).
        required: true
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
      - name: user_id
        in: path
        description: Generated path parameter for user_id
        required: true
        schema:
          type: string
  /cloudfiles/authorize:
    post:
      responses:
        default:
          description: Default response for POST /cloudfiles/authorize
      security:
        - siemensid-authorizer: []
  /cloudfiles/heartbeat:
    post:
      responses:
        '200':
          description: Heartbeat accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for POST /cloudfiles/heartbeat
      security:
        - siemensid-authorizer: []
      summary: Send cloudfiles heartbeat
      description: Sends heartbeat for an active cloudfiles upload session.
      operationId: sendHeartbeat
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HeartbeatRequest'
        description: Upload heartbeat payload.
        required: true
  /creditkey/redeem:
    post:
      responses:
        default:
          description: Default response for POST /creditkey/redeem
      security:
        - siemensid-authorizer: []
  /creditkey/simulate-order:
    post:
      responses:
        default:
          description: Default response for POST /creditkey/simulate-order
      security:
        - siemensid-authorizer: []
  /me/prefs:
    post:
      responses:
        '200':
          description: Preferences updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for POST /me/prefs
      security:
        - siemensid-authorizer: []
      summary: Update user preferences
      description: Updates user preferences such as preferred tenant.
      operationId: updatePreferences
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PreferencesUpdateRequest'
        description: Preference payload.
        required: true
  /me/viewAnnouncements:
    post:
      responses:
        '200':
          description: Announcement view date updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for POST /me/viewAnnouncements
      security:
        - siemensid-authorizer: []
      summary: Update announcement view date
      description: Stores the timestamp of the last viewed announcement.
      operationId: updateLastAnnouncementViewedDate
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ViewAnnouncementsRequest'
        description: Announcement date payload.
        required: true
  /prepaidtenant:
    post:
      responses:
        default:
          description: Default response for POST /prepaidtenant
      security:
        - siemensid-authorizer: []
  /subscriptiontenant:
    post:
      responses:
        '200':
          description: Subscription tenant created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubscriptionData'
        default:
          description: Default response for POST /subscriptiontenant
      security:
        - siemensid-authorizer: []
      summary: Create subscription tenant
      description: Creates a tenant for a subscription flow.
      operationId: createSubscriptionTenant
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionTenantCreateRequest'
        description: Subscription tenant creation payload.
        required: true
  /tenants/{tenant_id}/pricingplan:
    post:
      responses:
        '200':
          description: Created pricing plan.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingPlan'
        default:
          description: Default response for POST /tenants/{tenant_id}/pricingplan
      security:
        - siemensid-authorizer: []
      summary: Create pricing plan
      description: Creates a pricing plan for the selected tenant.
      operationId: createPricingPlan
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PricingPlan'
        description: Pricing plan data from UI pricing plan form.
        required: true
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /projects/{project_id}/state:
    put:
      responses:
        '200':
          description: Project state changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for PUT /projects/{project_id}/state
      security:
        - siemensid-authorizer: []
      summary: Change project state
      description: Starts or stops a project workload type.
      operationId: changeProjectState
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectStateChangeRequest'
        description: Project state change payload.
        required: true
    parameters:
      - name: project_id
        in: path
        description: Generated path parameter for project_id
        required: true
        schema:
          type: string
  /subscriptionInfo:
    put:
      responses:
        '200':
          description: Subscription data.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubscriptionData'
        default:
          description: Default response for PUT /subscriptionInfo
      security:
        - siemensid-authorizer: []
      summary: Get subscription data
      description: >-
        Returns subscription details and associated tenant for current user
        context.
      operationId: getSubscriptionData
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenericResponse'
        description: Empty object payload used by UI.
        required: true
  /tenants/updateTenant/{tenant_id}:
    put:
      responses:
        '200':
          description: Tenant updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericResponse'
        default:
          description: Default response for PUT /tenants/updateTenant/{tenant_id}
      security:
        - siemensid-authorizer: []
      summary: Update tenant settings
      description: Updates tenant level display cost setting.
      operationId: updateTenant
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TenantUpdateRequest'
        description: Tenant update payload.
        required: true
    parameters:
      - name: tenant_id
        in: path
        description: Generated path parameter for tenant_id
        required: true
        schema:
          type: string
  /projects/{project_id}/simitcosts:
    get:
      summary: Get SIMIT library usage
      description: Returns current day SIMIT library usage/cost information for a project.
      security:
        - siemensid-authorizer: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimitCostResponse'
          description: Current day SIMIT usage.
      operationId: getSimitLibUsageCurrentDay
    parameters:
      - description: Project identifier
        name: project_id
        in: path
        schema:
          type: string
        required: true
components:
  securitySchemes:
    siemensid-authorizer:
      type: oauth2
      flows: {}
      x-amazon-apigateway-authorizer:
        identitySource: $request.header.Authorization
        jwtConfiguration:
          audience:
            - https://pcsneo-saas.sicue.siemens.cloud/
            - https://siemens-qa-00290.eu.auth0.com/userinfo
          issuer: https://siemens-qa-00290.eu.auth0.com/
        type: jwt
  schemas:
    # ── Pricing ────────────────────────────────────────────────────────────────
    PricingPlan:
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        neoCreditsPerHour:
          type: number
        neoCreditsPerDay:
          type: number
        tenantId:
          format: int64
          type: integer
          nullable: true
        simitCreditsPerHour:
          type: number
      type: object
      required:
        - id
        - name
        - neoCreditsPerHour
        - neoCreditsPerDay
        - simitCreditsPerHour

    # ── Subtenants ─────────────────────────────────────────────────────────────
    SubTenant:
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        pricingPlanId:
          type: integer
          format: int64
        pricingPlanName:
          type: string
      required:
        - id
        - name
        - pricingPlanId
    SubTenantCreateRequest:
      type: object
      properties:
        name:
          type: string
        pricingPlanId:
          type: integer
          format: int64
        userEmail:
          type: string
          format: email
        userFirstName:
          type: string
        userLastName:
          type: string
        copyParentPricingPlan:
          type: boolean
      required:
        - name
        - pricingPlanId
        - userEmail
        - userFirstName
        - userLastName
    SubTenantUpdateRequest:
      type: object
      properties:
        pricingPlanId:
          type: integer
          format: int64
      required:
        - pricingPlanId

    # ── Tenant users ───────────────────────────────────────────────────────────
    TenantUserProfile:
      type: object
      description: >-
        Tenant-scoped profile attached to a user.
        role: 0=User, 1=Admin, 2=Owner.
        userDisplayCosts: 0=No, 1=Yes.
      properties:
        tenantId:
          type: integer
          format: int64
        tenantName:
          type: string
        role:
          type: integer
          description: "0=User, 1=Admin, 2=Owner"
          enum: [0, 1, 2]
        userDisplayCosts:
          type: integer
          description: "0=No, 1=Yes"
          enum: [0, 1]
        displayCosts:
          type: boolean
        capabilities:
          type: array
          items:
            type: string
        hierarchicalName:
          type: string
        tenantEmailOfOrder:
          type: string
        suspendedAt:
          type: string
          nullable: true
    TenantUser:
      type: object
      description: >-
        A user as returned by GET /tenants/{tenant_id}/users.
        Field names are camelCase (firstName, lastName, tenantProfile).
      properties:
        id:
          type: integer
          format: int64
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
          format: email
        tenantProfile:
          $ref: '#/components/schemas/TenantUserProfile'
    TenantUserAddRequest:
      type: object
      description: >-
        Body for POST /tenants/{tenant_id}/users.
        role: 0=User, 1=Admin, 2=Owner.
        userDisplayCosts: 0=No, 1=Yes.
        portal_env is always "dev" in current tooling.
      properties:
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
          format: email
        role:
          type: integer
          description: "0=User, 1=Admin, 2=Owner"
          enum: [0, 1, 2]
        userDisplayCosts:
          type: integer
          description: "0=No, 1=Yes"
          enum: [0, 1]
        portal_env:
          type: string
          description: Environment tag, always "dev" in current tooling.
          example: dev
      required:
        - firstName
        - lastName
        - email
        - role
    TenantUserUpdateRequest:
      type: object
      description: >-
        Body for PUT /tenants/{tenant_id}/users/{user_id}.
        Observed usage: only role is sent; userDisplayCosts is optional.
      properties:
        role:
          type: integer
          description: "0=User, 1=Admin, 2=Owner"
          enum: [0, 1, 2]
        userDisplayCosts:
          type: integer
          description: "0=No, 1=Yes"
          enum: [0, 1]
    TenantUserRemoveRequest:
      type: object
      description: >-
        Body for PATCH /tenants/{tenant_id}/users/{user_id}.
        Scripts send an empty object {}. env field present in schema but not
        observed in script calls.
      properties:
        env:
          type: string
    TenantUserRemovalInfo:
      type: object
      properties:
        allowRemoval:
          type: boolean
        projects:
          type: array
          items:
            type: string

    # ── Current user (/me) ─────────────────────────────────────────────────────
    AuthPortalUser:
      type: object
      description: >-
        Response from GET /me. Contains the logged-in user's profile and
        all tenant memberships.
      properties:
        id:
          type: integer
          format: int64
          description: Portal user ID.
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
          format: email
        prefTenant:
          type: integer
          format: int64
          description: ID of the user's preferred (default) tenant.
        tenantProfiles:
          type: array
          description: One entry per tenant the user belongs to.
          items:
            $ref: '#/components/schemas/TenantProfile'
      required:
        - id
        - firstName
        - lastName
        - email
    TenantProfile:
      type: object
      description: >-
        Summary of a tenant membership as embedded in /me response.
        role: 0=User, 1=Admin, 2=Owner.
      properties:
        tenantId:
          type: integer
          format: int64
        tenantName:
          type: string
        role:
          type: integer
          description: "0=User, 1=Admin, 2=Owner"
          enum: [0, 1, 2]
        remainingCredits:
          type: number
          description: Credits remaining in the tenant.
        lifecycleState:
          type: string
          description: Current lifecycle state of the tenant.
      required:
        - tenantId
        - tenantName
        - role

    # ── Projects ───────────────────────────────────────────────────────────────
    ProjectCreateRequest:
      type: object
      description: >-
        Body for POST /tenants/{tenant_id}/projects.
        Observed in both Python and PowerShell bulk provisioners.
        projectCreditBudget may be null to indicate no budget limit.
        automaticStopTime is in minutes.
      properties:
        name:
          type: string
          description: Project display name.
        selectedPackageVersion:
          type: string
          description: >-
            PCS neo / SIMIT version string, e.g.
            "PCS neo V7.0.0.0  -  SIMIT V11.3.2"
          example: "PCS neo V7.0.0.0  -  SIMIT V11.3.2"
        members:
          type: array
          description: Users to add as project members at creation time.
          items:
            $ref: '#/components/schemas/ProjectMember'
        automaticStopActive:
          type: boolean
          description: Whether automatic stop is enabled for this project.
        automaticStopTime:
          type: integer
          description: Idle time in minutes before automatic stop triggers.
          example: 30
        projectCreditBudget:
          type: integer
          nullable: true
          description: Credit budget for the project, or null for no limit.
      required:
        - name
        - selectedPackageVersion
    ProjectMember:
      type: object
      description: >-
        A project member entry used in ProjectCreateRequest.members.
        role: 0=Member, 1=Admin.
      properties:
        userId:
          type: integer
          format: int64
          description: Tenant user ID (from GET /tenants/{tenant_id}/users).
        role:
          type: integer
          description: "0=Member, 1=Admin"
          enum: [0, 1]
        simitEnabled:
          type: boolean
          description: Whether SIMIT is enabled for this member.
        neoUsername:
          type: string
          description: PCS neo username for this member.
      required:
        - userId
        - role
    ProjectExternal:
      type: object
      description: >-
        A project as returned by GET /tenants/{tenant_id}/projects.
        Core fields are known; additional fields may be present.
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        selectedPackageVersion:
          type: string
          description: >-
            PCS neo / SIMIT version string, e.g.
            "PCS neo V7.0.0.0  -  SIMIT V11.3.2"
        automaticStopActive:
          type: boolean
        automaticStopTime:
          type: integer
          description: Idle time in minutes before automatic stop triggers.
        projectCreditBudget:
          type: integer
          nullable: true
      additionalProperties: true
    ProjectUpdateRequest:
      type: object
      additionalProperties: true
    ProjectStateChangeRequest:
      type: object
      properties:
        type:
          type: string
          enum:
            - NEO
            - SIMIT
      required:
        - type
    ProjectInstanceLog:
      type: object
      properties:
        instance:
          type: string
        timestamp:
          type: string
          format: date-time
        status:
          type: string
        userEmail:
          type: string

    # ── Tenant settings ────────────────────────────────────────────────────────
    TenantUpdateRequest:
      type: object
      properties:
        displayCosts:
          type: boolean
      required:
        - displayCosts

    # ── Announcements ──────────────────────────────────────────────────────────
    AnnouncementContentItem:
      type: object
      properties:
        contentType:
          type: string
          enum:
            - section-header
            - paragraph
            - image
            - button
            - list
        value:
          type: array
          items:
            type: string
    Announcement:
      type: object
      properties:
        title:
          type: string
        shortCardTitle:
          type: string
        subtitle:
          type: string
        contentItems:
          type: array
          items:
            $ref: '#/components/schemas/AnnouncementContentItem'
        viewed:
          type: boolean
        created:
          type: string
        modified:
          type: string

    # ── Preferences ────────────────────────────────────────────────────────────
    PreferencesUpdateRequest:
      type: object
      properties:
        pref_tenant_id:
          type: integer
          format: int64
      required:
        - pref_tenant_id
    ViewAnnouncementsRequest:
      type: object
      properties:
        date:
          type: string
      required:
        - date

    # ── Cloudfiles ─────────────────────────────────────────────────────────────
    HeartbeatRequest:
      type: object
      properties:
        filename:
          type: string
        clientSession:
          type: string
      required:
        - clientSession
        - filename

    # ── Parameter store ────────────────────────────────────────────────────────
    ParameterStoreFilesResponse:
      oneOf:
        - type: array
          items:
            type: string
          description: List of available package version strings.
        - type: object
          properties:
            statusCode:
              type: string
            body:
              type: string

    # ── Subscription ───────────────────────────────────────────────────────────
    SubscriptionInfo:
      type: object
      additionalProperties: true
    SubscriptionData:
      type: object
      properties:
        subscriptionTenant:
          type: object
          nullable: true
          additionalProperties: true
        subscriptionData:
          $ref: '#/components/schemas/SubscriptionInfo'
    SubscriptionTenantCreateRequest:
      type: object
      properties:
        tenantName:
          type: string
      required:
        - tenantName

    # ── Credits / costs ────────────────────────────────────────────────────────
    CreditMonthlyExport:
      type: object
      additionalProperties: true
    PriceListResponse:
      type: object
      additionalProperties: true
    SimitCostResponse:
      type: object
      additionalProperties: true
    SimitUploadHistoryItem:
      type: object
      additionalProperties: true

    # ── Generic ────────────────────────────────────────────────────────────────
    GenericResponse:
      type: object
      additionalProperties: true
