openapi: 3.0.0
info:
  title: KPI API V3 - ValueStreamer
  version: 3.0.0
  description: "This is the specification of the REST interface for data exchange with third-party systems."
servers:
  - url: https://api-tenant.valuestreamer.de/api
security:
  - basicAuth: []
tags:
- name: "KPI"
  description: ""
- name: "Sub-tile"
  description: ""

paths:
  /exchange/kpi-data:
    get:
      tags:
        - "KPI"
        - "Sub-tile"
      summary: "Returns a list of filterable data records for a KPI."
      description: "This endpoint can be used to get a series of data points for a KPI over a defined period of time, ordered by date DESC. <br/><br/>
      Each value entry contains forecast=true when it represents either an actual forecast or a target forecast.<br/><br/>
      If the input frequency is DAY, from and to must not be more than 31 days apart. E.g. from 2021-01-01 to 2021-01-31. <br/><br/>
      If the input frequency is WEEK, from and to must not be more than 6 months apart. E.g. from 2021-01-01 to 2021-06-30.<br/><br/>
      If the input frequency is MONTH, from and to must not be more than 24 months apart. E.g. from 2020-01-01 to 2022-12-01"
      parameters:
        - in: "query"
          name: "kpi-id"
          description: "UUID of the KPI."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "query"
          name: "from"
          description: "Start date of the time series."
          required: true
          schema:
            type: "string"
            format: "date"
        - in: "query"
          name: "to"
          description: "End date of the time series."
          required: true
          schema:
            type: "string"
            format: "date"
        - in: "query"
          name: "team-id"
          description: "UUID of the team."
          required: false
          schema:
            type: "string"
            format: "uuid"
        - in: "query"
          name: "sub-tile-id"
          description: "UUID of the sub-tile."
          schema:
            type: "string"
            format: "uuid"
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/KpiData'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /exchange/kpi-data/{date}/{team-id}/{kpi-id}:
    get:
      tags:
        - "KPI"
      summary: "Get a specific record."
      parameters:
        - in: "path"
          name: "date"
          description: "Date of the record as YYYY-MM-DD."
          required: true
          schema:
            type: "string"
            format: "date"
            example: "2021-01-01"
        - in: "path"
          name: "team-id"
          description: "UUID of the team."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "kpi-id"
          description: "UUID of the kpi."
          required: true
          schema:
            type: "string"
            format: "uuid"
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                $ref: '#/components/schemas/KpiData'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - "KPI"
      summary: "Create or update a record."
      parameters:
        - in: "path"
          name: "date"
          description: "Date of the record as YYYY-MM-DD."
          required: true
          schema:
            type: "string"
            format: "date"
            example: "2021-01-01"
        - in: "path"
          name: "team-id"
          description: "UUID of the team."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "kpi-id"
          description: "UUID of the kpi."
          required: true
          schema:
            type: "string"
            format: "uuid"
      requestBody:
        required: true
        content:
          application/vs.v3.0+json:
            schema:
              type: object
              description: |
                Use this to create or update a data set in a specific kpi + team + date. Pass the necessary actual values and target values in the values array.
                Whether a submitted actual value is stored as an actual forecast depends on the team's timezone and the date (today or tomorrow, this week or next week, this month or next month) and the KPI's kpiEnteringDefaultPeriod flag:
                - When CURRENT, only periods strictly after the current day / week / month (in the team timezone) are forecast periods. Submitted actuals for the current period are stored as adopted actuals.
                - When PREVIOUS, the current day / week / month (in the team timezone) is also treated as a forecast period and submitted actuals are stored as actual forecasts.
                Set adoptActualsIfPossible=true to adopt existing actual forecasts once the reference period is no longer a forecast period (per the rule above); if adoption is not possible, the flag is silently ignored.
              properties:
                values:
                  type: array
                  items:
                    type: object
                    properties:
                      kpiValueId:
                        type: string
                        format: uuid
                      value:
                        type: number
                        format: 'double'
                        nullable: true
                    required:
                      - kpiValueId
                adoptActualsIfPossible:
                  type: boolean
                  default: false
                  description: "When true, adopts existing actual forecasts for this KPI data record once the reference period is no longer a forecast period (see the request body description above for the rule). A non-null submitted value for the same kpiValueId becomes the adopted current value. If the values are already adopted, absent, or still in a forecast period, the flag is ignored without an error."
              required:
                - values
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                $ref: '#/components/schemas/KpiData'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - "KPI"
      summary: "Delete a specific record."
      parameters:
        - in: "path"
          name: "date"
          description: "Date of the record as YYYY-MM-DD."
          required: true
          schema:
            type: "string"
            format: "date"
            example: "2021-01-01"
        - in: "path"
          name: "team-id"
          description: "UUID of the team."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "kpi-id"
          description: "UUID of the kpi."
          required: true
          schema:
            type: "string"
            format: "uuid"
      responses:
        '204':
          description: Record deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /exchange/kpi-data/{date}/{team-id}/{kpi-id}/{sub-tile-id}:
    get:
      tags:
        - "Sub-tile"
      summary: "Get a specific data record."
      parameters:
        - in: "path"
          name: "date"
          description: "Date of the record as YYYY-MM-DD."
          required: true
          schema:
            type: "string"
            format: "date"
            example: "2021-01-01"
        - in: "path"
          name: "team-id"
          description: "UUID of the team."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "kpi-id"
          description: "UUID of the kpi."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "sub-tile-id"
          description: "UUID of the sub-tile."
          required: true
          schema:
            type: "string"
            format: "uuid"
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                $ref: '#/components/schemas/KpiData'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      tags:
        - "Sub-tile"
      summary: "Create or update a specific record."
      parameters:
        - in: "path"
          name: "date"
          description: "Date of the record as YYYY-MM-DD."
          required: true
          schema:
            type: "string"
            format: "date"
            example: "2021-01-01"
        - in: "path"
          name: "team-id"
          description: "UUID of the team."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "kpi-id"
          description: "UUID of the kpi."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "sub-tile-id"
          description: "UUID of the sub-tile."
          required: true
          schema:
            type: "string"
            format: "uuid"
      requestBody:
        required: true
        content:
          application/vs.v3.0+json:
            schema:
              type: object
              description: |
                Use this to create or update a data set in a specific kpi + team + date. Pass the necessary actual values and target values in the values array.
                Whether a submitted actual value is stored as an actual forecast depends on the team's timezone and the date (today or tomorrow, this week or next week, this month or next month) and the KPI's kpiEnteringDefaultPeriod flag:
                - When CURRENT, only periods strictly after the current day / week / month (in the team timezone) are forecast periods. Submitted actuals for the current period are stored as adopted actuals.
                - When PREVIOUS, the current day / week / month (in the team timezone) is also treated as a forecast period and submitted actuals are stored as actual forecasts.
                Set adoptActualsIfPossible=true to adopt existing actual forecasts once the reference period is no longer a forecast period (per the rule above); if adoption is not possible, the flag is silently ignored.
              properties:
                values:
                  type: array
                  items:
                    type: object
                    properties:
                      kpiValueId:
                        type: string
                        format: uuid
                      value:
                        type: number
                        format: 'double'
                        nullable: true
                    required:
                      - kpiValueId
                adoptActualsIfPossible:
                  type: boolean
                  default: false
                  description: "When true, adopts existing actual forecasts for this KPI data record once the reference period is no longer a forecast period (see the request body description above for the rule). A non-null submitted value for the same kpiValueId becomes the adopted current value. If the values are already adopted, absent, or still in a forecast period, the flag is ignored without an error."
              required:
                - values
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                $ref: '#/components/schemas/KpiData'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - "Sub-tile"
      summary: "Delete a specific record."
      parameters:
        - in: "path"
          name: "date"
          description: "Date of the record as YYYY-MM-DD."
          required: true
          schema:
            type: "string"
            format: "date"
            example: "2021-01-01"
        - in: "path"
          name: "team-id"
          description: "UUID of the team."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "kpi-id"
          description: "UUID of the kpi."
          required: true
          schema:
            type: "string"
            format: "uuid"
        - in: "path"
          name: "sub-tile-id"
          description: "UUID of the sub-tile."
          required: true
          schema:
            type: "string"
            format: "uuid"
      responses:
        '204':
          description: Record deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'



  /exchange/kpi:
    get:
      tags:
        - "Meta"
      summary: "Returns the list of tenant-specific KPI."
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                        type: string
                        format: uuid
                    name:
                        type: string     
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /exchange/kpi/{kpi-id}:
    get:
      tags:
        - "Meta"
      summary: "Returns the details of a KPI by UUID."
      parameters:
        - in: "path"
          name: "kpi-id"
          description: "UUID of the KPI."
          required: true
          schema:
            type: "string"
      responses:
        '200':
          description: OK
          content:
            application/vs.v3.0+json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                    name:
                      type: string
                    kpiValueIsInteger:
                      type: boolean
                    kpiValueTimeReference:
                      type: string
                      enum: [DAY, WEEK, MONTH]
                      description: "Defines whether the data point is assigned to a day, a calendar week or a month."
                      default: "DAY"
                    kpiEnteringDefaultPeriod:
                      type: string
                      enum: [ CURRENT, PREVIOUS ]
                      description: "Default date period the KPI entering form is showing when opened.
                       - CURRENT when the default period is today / this week / this month (in the team's timezone). Entries for dates after the CURRENT period are treated as forecasts.
                       - PREVIOUS when the default period is yesterday / last week / last month (in the team's timezone). Entries for dates from today / this week / this month onward are treated as forecasts.
                       "
                    targetValueType:
                      type: string
                      enum: [NULL, ONE, MINMAX]
                      description: ""
                      default: "ONE"
                    targetValueScope:
                      type: string
                      enum: [ NULL, TEAM, GLOBAL ]
                      description: "NULL = KPI has no target. TEAM = Target can be modified in each team. GLOBAL = Target applies globally to every team and can not be modified via API."
                      default: "TEAM"
                    targetValueGlobalSingle:
                      type: number
                      description: "When the KPI has a single target value, this field contains the global target value as number."
                      example: 10
                    targetValueGlobalMin:
                      type: number
                      description: "When the KPI has a target min/max corridor, this field contains the global target MIN value as number."
                      example: 50
                    targetValueGlobalMax:
                      type: number
                      description: "When the KPI has a target min/max corridor, this field contains the global target MAX value as number."
                      example: 100
                    actualValueResultCalculationLogic:
                      type: string
                      enum: [NULL, ADDITION, MULTIPLICATION, DIVISION]
                      description: "Defines how the given values are offset against each other."
                      default: NULL
                    kpiValues:
                      type: array
                      items:
                        type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                          name:
                            type: string
                          orderId:
                            type: integer
                            description: "Order of the values. Relevant for calculation/offset Division: Division: Actual1/Actual2."
                    recordingTeams:
                      type: array
                      items:
                        type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                          name:
                            type: string
                          subTileRecording:
                            type: boolean
                            default: false
                            description: "FALSE: The team has no sub-KPI. Data can be entered directly in the team.
                            TRUE: The team has sub-KPI. Data must be inserted for the combination of Team+KPI+Sub-Tile."
                          subTiles:
                            type: array
                            items:
                              type: object
                              properties:
                                id:
                                  type: string
                                  format: uuid
                                name:
                                  type: string
                                orderId:
                                  type: integer
                                  description: "Order of the sub-tiles."



        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalServerError'







components:
  securitySchemes:
    basicAuth:           
      type: http
      scheme: basic
  schemas:
    KpiData:
      type: "object"
      properties:
        meta: 
          type: object
          properties:
            kpiId:
              type: string
              format: uuid
              description: "UUID of the related KPI"
            teamId:
              type: string
              format: uuid
              description: "UUID of the related team"
            subTileId:
              type: string
              format: uuid
              description: "UUID of the related sub tile"
              nullable: true
            date:
              type: string
              format: 'date'
              description: "Time reference of the value"
          required:
          - "kpiId"
          - "teamId"
          - "date"
        values:
          type: array
          items: 
            type: object
            properties:
              kpiValueId:
                type: string
                format: uuid
                description: "UUID of the value type [ACTUAL/TARGET]"
              value:
                type: number
                format: 'double'
                description: "Recorded value / measure."
              forecast:
                type: boolean
                readOnly: true
                description: "TRUE when this value is a forecast. Actual forecasts where created for a date in the future and are not yet adopted. Target forecasts are target values where the related actual value is absent or null, or where the record date is considered a forecast period."
            required:
              - "kpiValueId"
              - "value"
              - "forecast"
        result:
          type: "number"
          format: ""
          readOnly: true
          nullable: true
          description: "If the actual values are offset against each other, then this field contains the result."
        historicalForecastValues:
          type: array
          readOnly: true
          description: "Contains the forecast values before adoption."
          items:
            type: object
            properties:
              kpiValueId:
                type: string
                format: uuid
                description: "UUID of the value type [ACTUAL/TARGET]"
              value:
                type: number
                format: 'double'
                description: "Historical forecast value."
            required:
              - "kpiValueId"
              - "value"
        historicalForecastResult:
          type: "number"
          format: ""
          readOnly: true
          nullable: true
          description: "Historical calculated forecast result."
      required:
          - "meta"
          - "values"
    Page:
      type: object
      readOnly: true
      properties:
        page:
          type: integer
          description: "Number of the current page"
        elementsOnPage:
          type: integer
          description: "Number of elements on the current page"
        elementsPerPage:
          type: integer
          description: "Number of elements per page"
        totalPages:
          type: integer
          description: "Number of all pages"
        totalElements:
          type: integer
          description: "Number of all elements"
    # Schema for error response body
    Error:
      type: object
      properties:
        errorId:
          type: number
        errorMessage:
          type: string

  responses:
    NotFound:
      description: Resource not found
      content:
        application/vs.v3.0+json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Not authorized
      content:
        application/vs.v3.0+json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: Bad request
      content:
        application/vs.v3.0+json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: 'Conflict is returned if a value already exists for the combination of kpi, sub tile (optional), team, value type and date. The response contains the existing object.'
      content:
        application/vs.v3.0+json:
          schema:
            type: object
            properties:
              errorId:
                type: number
              errorMessage:
                type: string
              kpiData:
                allOf:
                - $ref: '#/components/schemas/KpiData'
    InternalServerError:
      description: Internal server error
      content:
        application/vs.v3.0+json:
          schema:
            $ref: '#/components/schemas/Error'
    UnsupportedMediaType:
      description: UnsupportedMediaType
      content:
        application/vs.pb.v1.0.0+json:
          schema:
            $ref: '#/components/schemas/Error'
