openapi: 3.0.3
info:
  title: Anomalies API
  description:
    $ref: './md-partials/anomalies-intro.mdx'
  version: 1.0.0

servers:
  - url: https://app.nobl9.com/api/anomalies/v1
    description: EU instance
  - url: https://us1.nobl9.com/api/anomalies/v1
    description: US instance

tags:
  - name: Data anomalies
    description: Find affected SLOs and retrieve anomaly intervals.

security:
  - basicAuth: []
  - bearerAuth: []

paths:
  /affected-slos:
    post:
      operationId: getAffectedSlos
      summary: List SLOs affected by anomalies
      description: |
        Find SLOs with anomalies in a selected time range.

        Returns one aggregate item for each SLO visible to the authenticated user
        or access key with matching anomalies in the requested metric-time range.
        Use `POST /anomalies` to inspect the individual intervals for one SLO.
      tags:
        - Data anomalies
      security:
        - basicAuth: []
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AffectedSLOsRequest"
            example:
              projects: ["*"]
              from: "2026-08-01T00:00:00Z"
              to: "2026-08-02T00:00:00Z"
              includeLive: true
              includeReplay: true
      responses:
        "200":
          description: Affected SLOs retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AffectedSLOsResponse"
              example:
                items:
                  - project: my-project
                    service: checkout
                    slo: checkout-availability
                    anomalyCount: 1
                    anomalyCountsByStatus:
                      open: 0
                      resolved: 1
                    anomalyCountsBySource:
                      live: 0
                      replay: 1
                    anomalyCountsByCategory:
                      GoodOverTotal: 1
                timeRange:
                  from: "2026-08-01T00:00:00Z"
                  to: "2026-08-02T00:00:00Z"
                includeResolved: true
        "400":
          description: Invalid request parameters or page token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Invalid or missing authentication
        "403":
          description: Automatic data anomaly detection is not enabled
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /anomalies:
    post:
      operationId: getAnomalies
      summary: List anomalies for an SLO
      description: |
        Retrieve anomaly intervals for a selected SLO.

        Returns anomaly intervals for one SLO visible to the authenticated user or
        access key. Completed Replay results replace live
        and older Replay results in the metric-time ranges they cover. Returned
        items retain their original detection and resolution times. A clean Replay
        does not produce an item.
      tags:
        - Data anomalies
      security:
        - basicAuth: []
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnomaliesRequest"
            example:
              slo: my-project/checkout-availability
              from: "2026-08-01T00:00:00Z"
              to: "2026-08-02T00:00:00Z"
              includeLive: true
              includeReplay: true
      responses:
        "200":
          description: Anomalies retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AnomaliesResponse"
              example:
                items:
                  - anomalyId: f47ac10b-58cc-4372-a567-0e02b2c3d479
                    type: GoodOverTotal
                    objective: availability
                    status: resolved
                    source: replay
                    replayStartedAt: "2026-08-03T09:00:00Z"
                    affectedTimeRange:
                      from: "2026-08-01T11:30:00Z"
                      to: "2026-08-01T11:35:00Z"
                timeRange:
                  from: "2026-08-01T00:00:00Z"
                  to: "2026-08-02T00:00:00Z"
                includeResolved: true
                project: my-project
                service: checkout
                slo: checkout-availability
        "400":
          description: Invalid request parameters or page token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Invalid or missing authentication
        "403":
          description: Automatic data anomaly detection is not enabled
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: >-
        Authenticate with a Nobl9 access key. Use the Client ID as the username
        and the Client Secret as the password.
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Authenticate with a Nobl9 access token in the `Authorization` header.

  schemas:
    AffectedSLOsRequest:
      type: object
      required:
        - from
      properties:
        projects:
          type: array
          items:
            type: string
          description: >-
            Project names. Use `["*"]` by itself to search all projects visible
            to the authenticated user or access key.
          example: ["my-project"]
        services:
          type: array
          items:
            type: string
          description: Service names formatted as `project/service`.
          example: ["my-project/checkout"]
        slos:
          type: array
          items:
            type: string
          description: SLO names formatted as `project/slo`.
          example: ["my-project/checkout-availability"]
        labels:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: SLO labels and the values to match.
          example:
            team: ["payments"]
        from:
          type: string
          format: date-time
          description: >-
            Inclusive start of the metric-time range. The requested range must be
            at least one minute and cannot exceed two years.
          example: "2026-08-01T00:00:00Z"
        to:
          type: string
          format: date-time
          description: >-
            Exclusive end of the metric-time range. Defaults to the current time.
          example: "2026-08-02T00:00:00Z"
        anomalyTypes:
          $ref: "#/components/schemas/AnomalyTypeFilter"
        includeResolved:
          type: boolean
          default: true
          description: Include resolved anomalies.
        includeLive:
          type: boolean
          default: true
          description: >-
            Include continuously detected anomalies. Set to `false` to return
            only Replay findings when `includeReplay` is enabled.
        includeReplay:
          type: boolean
          default: false
          description: >-
            Include completed Replay findings in the combined result.
            Replay results replace live and older Replay results in the
            metric-time ranges they cover.
        timeRangeMode:
          $ref: "#/components/schemas/TimeRangeMode"
        pageSize:
          type: integer
          minimum: 1
          maximum: 100
          default: 50
          description: Maximum number of affected SLOs to return.
        pageToken:
          type: string
          description: Opaque token returned by the preceding page.

    AnomaliesRequest:
      type: object
      required:
        - slo
        - from
      properties:
        slo:
          type: string
          description: SLO name formatted as `project/slo`.
          example: my-project/checkout-availability
        from:
          type: string
          format: date-time
          description: >-
            Inclusive start of the metric-time range. The requested range must be
            at least one minute and cannot exceed two years.
          example: "2026-08-01T00:00:00Z"
        to:
          type: string
          format: date-time
          description: >-
            Exclusive end of the metric-time range. Defaults to the current time.
          example: "2026-08-02T00:00:00Z"
        anomalyTypes:
          $ref: "#/components/schemas/AnomalyTypeFilter"
        includeResolved:
          type: boolean
          default: true
          description: Include resolved anomalies.
        includeLive:
          type: boolean
          default: true
          description: >-
            Include continuously detected anomalies. Set to `false` to return
            only Replay findings when `includeReplay` is enabled.
        includeReplay:
          type: boolean
          default: false
          description: >-
            Include completed Replay findings in the combined result.
            Replay results replace live and older Replay results in the
            metric-time ranges they cover.
        timeRangeMode:
          $ref: "#/components/schemas/TimeRangeMode"
        pageSize:
          type: integer
          minimum: 1
          maximum: 100
          default: 50
          description: Maximum number of anomaly intervals to return.
        pageToken:
          type: string
          description: Opaque token returned by the preceding page.

    AnomalyTypeFilter:
      type: array
      description: Public anomaly type names. Omit to include all types.
      items:
        $ref: "#/components/schemas/AnomalyType"

    AnomalyType:
      type: string
      enum:
        - NoData
        - IncrementalViolation
        - NoBurn
        - ConstantBurn
        - GoodOverTotal
      example: GoodOverTotal

    TimeRangeMode:
      type: string
      enum:
        - detected
        - overlap
      default: overlap
      description: >-
        `overlap` includes an anomaly when any part remains in the requested range
        after Replay results are applied. `detected` includes it only when its
        original detection time remains in the range after Replay results are applied.

    AffectedSLOsResponse:
      type: object
      required:
        - items
        - timeRange
        - includeResolved
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/AffectedSLO"
        nextPageToken:
          type: string
          description: Opaque token for the next page. Omitted on the last page.
        timeRange:
          $ref: "#/components/schemas/TimeRange"
        includeResolved:
          type: boolean
          description: Whether anomalies whose original status is resolved were included.

    AffectedSLO:
      type: object
      required:
        - project
        - service
        - slo
        - anomalyCount
        - anomalyCountsByStatus
        - anomalyCountsBySource
        - anomalyCountsByCategory
      properties:
        project:
          type: string
          example: my-project
        service:
          type: string
          example: checkout
        slo:
          type: string
          example: checkout-availability
        anomalyCount:
          type: integer
          format: int64
          minimum: 0
          example: 1
        anomalyCountsByStatus:
          type: object
          description: Number of returned anomalies grouped by resolution status.
          required:
            - open
            - resolved
          additionalProperties: false
          properties:
            open:
              type: integer
              format: int64
              minimum: 0
            resolved:
              type: integer
              format: int64
              minimum: 0
        anomalyCountsBySource:
          type: object
          description: Number of returned anomalies grouped by source.
          required:
            - live
            - replay
          additionalProperties: false
          properties:
            live:
              type: integer
              format: int64
              minimum: 0
            replay:
              type: integer
              format: int64
              minimum: 0
        anomalyCountsByCategory:
          type: object
          description: Number of returned anomalies grouped by public anomaly type.
          additionalProperties:
            type: integer
            format: int64
            minimum: 0

    AnomaliesResponse:
      type: object
      required:
        - items
        - timeRange
        - includeResolved
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Anomaly"
        nextPageToken:
          type: string
          description: Opaque token for the next page. Omitted on the last page.
        timeRange:
          $ref: "#/components/schemas/TimeRange"
        includeResolved:
          type: boolean
          description: Whether anomalies whose original status is resolved were included.
        project:
          type: string
          description: Project containing the requested SLO.
          example: my-project
        service:
          type: string
          description: Service containing the requested SLO.
          example: checkout
        slo:
          type: string
          description: Name of the requested SLO.
          example: checkout-availability

    Anomaly:
      type: object
      required:
        - anomalyId
        - type
        - objective
        - status
        - source
        - affectedTimeRange
      properties:
        anomalyId:
          type: string
          format: uuid
        type:
          $ref: "#/components/schemas/AnomalyType"
        objective:
          type: string
          description: Name of the affected SLO objective.
          example: availability
        status:
          type: string
          enum:
            - open
            - resolved
        source:
          type: string
          enum:
            - live
            - replay
        replayStartedAt:
          type: string
          format: date-time
          description: >-
            Time when the Replay that produced this anomaly started. Present
            only when `source` is `replay`.
        affectedTimeRange:
          $ref: "#/components/schemas/AnomalyTimeRange"

    AnomalyTimeRange:
      type: object
      required:
        - from
      properties:
        from:
          type: string
          format: date-time
          description: Metric time when the anomaly was detected.
        to:
          type: string
          format: date-time
          description: >-
            Metric time when the anomaly was resolved. Omitted while the anomaly
            is open.

    TimeRange:
      type: object
      required:
        - from
        - to
      properties:
        from:
          type: string
          format: date-time
          description: Inclusive start of the requested metric-time range.
        to:
          type: string
          format: date-time
          description: Exclusive end of the requested metric-time range.

    ErrorResponse:
      type: object
      required:
        - message
        - statusCode
      properties:
        message:
          type: string
          description: Public description of the error.
          example: invalid request parameters
        error:
          type: string
          description: Additional validation detail when available.
          example: pageSize must be between 1 and 100
        statusCode:
          type: integer
          description: HTTP status code.
          example: 400
