openapi: 3.2.0
info:
  title: Meter Data Validation API
  version: "1.2.0"
  contact:
    name: Meter Data Validation Support
    email: support-provider-data-validation@flexidao.atlassian.net
  description: >
    Validation service for Meter Data for Bridge T-EACs files. Upload one or
    more XLSX or CSV files and receive structured reports of format and
    data-quality checks.

    Files are processed in memory only and are never stored.

    **Request limits**
      - Maximum file size: 8 MB
      - Maximum rows: 50,000 per file
      - Maximum files per batch upload: 20
      - Rate limit: 15 requests per minute per IP address
servers:
  - url: https://provider-meter-data-validation.cfesuite.com
    description: Production
paths:
  /api/validate:
    post:
      operationId: validateMeterData
      summary: Validate meter data file(s)
      description: >
        Validates XLSX or CSV meter-data files against all file, header, row,
        and time-series requirements.

        For backward compatibility, send a single file under the field name
        `file` to receive a `ValidationResult`.

        To validate up to 20 files in one request, send files under the field
        name `files` to receive a `ValidationBatchResult`.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: Legacy single XLSX or CSV file to validate (max 8 MB).
                files:
                  type: array
                  maxItems: 20
                  items:
                    type: string
                    format: binary
                  description: XLSX or CSV files to validate as a batch (max 8 MB each).
      responses:
        "200":
          description: >
            Validation completed. Single-file `file` requests return
            `ValidationResult`; multi-file `files` requests return
            `ValidationBatchResult`. Check `overallStatus` for the result.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ValidationResult"
                  - $ref: "#/components/schemas/ValidationBatchResult"
        "400":
          description: >
            Bad request — no file uploaded, too many files, an unreadable
            upload, or a file exceeds the 50,000 row limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "413":
          description: The uploaded file exceeds the 8 MB size limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded (more than 15 requests per minute per IP).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Unexpected server error while validating the file.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /health:
    get:
      operationId: healthCheck
      summary: Health check
      responses:
        "200":
          description: Service is healthy.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    examples: ["ok"]
components:
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message.
      required: [error]
    CheckStatus:
      type: string
      enum: [pass, fail, warn, skipped]
    CheckCategory:
      type: string
      enum: [file, header, row, time-series]
    RowIssue:
      type: object
      properties:
        row:
          type: integer
          description: 1-based row number in the source file.
        column:
          type: string
        value:
          type: string
        message:
          type: string
      required: [row, column, value, message]
    MeterLocalTimeRange:
      type: object
      properties:
        meterId:
          type: string
        timeRange:
          type: [object, "null"]
          properties:
            start:
              type: string
              description: Original datetime_start_local value for the first reading.
            end:
              type: string
              description: Original datetime_start_local value for the final reading.
          required: [start, end]
      required: [meterId, timeRange]
    ValidationCheck:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        category:
          $ref: "#/components/schemas/CheckCategory"
        status:
          $ref: "#/components/schemas/CheckStatus"
        message:
          type: string
        issues:
          type: array
          items:
            $ref: "#/components/schemas/RowIssue"
        totalIssues:
          type: integer
      required: [id, name, description, category, status, message, issues, totalIssues]
    ValidationSummary:
      type: object
      properties:
        fileName:
          type: string
        totalRows:
          type: integer
        facilityIds:
          type: array
          items:
            type: string
        meterIds:
          type: array
          items:
            type: string
        registryIds:
          type: array
          items:
            type: string
        timeRange:
          type: [object, "null"]
          properties:
            start:
              type: string
              format: date-time
            end:
              type: string
              format: date-time
          required: [start, end]
        meterLocalTimeRanges:
          type: array
          description: Local datetime ranges for each meter, preserving source offsets.
          items:
            $ref: "#/components/schemas/MeterLocalTimeRange"
        detectedInterval:
          type: [string, "null"]
      required:
        [fileName, totalRows, facilityIds, meterIds, registryIds, timeRange, meterLocalTimeRanges, detectedInterval]
    ValidationResult:
      type: object
      properties:
        overallStatus:
          type: string
          enum: [pass, fail]
        summary:
          $ref: "#/components/schemas/ValidationSummary"
        checks:
          type: array
          items:
            $ref: "#/components/schemas/ValidationCheck"
        uiUrl:
          type: [string, "null"]
          format: uri
          description: Self-contained link that opens this result in the validation UI.
      required: [overallStatus, summary, checks, uiUrl]
    ValidationBatchFileResult:
      type: object
      properties:
        fileName:
          type: string
        overallStatus:
          type: string
          enum: [pass, fail]
        result:
          $ref: "#/components/schemas/ValidationResult"
      required: [fileName, overallStatus, result]
    ValidationBatchResult:
      type: object
      properties:
        overallStatus:
          type: string
          enum: [pass, fail]
        fileCount:
          type: integer
        results:
          type: array
          items:
            $ref: "#/components/schemas/ValidationBatchFileResult"
        uiUrl:
          type: [string, "null"]
          format: uri
          description: Self-contained link that opens this batch result in the validation UI.
      required: [overallStatus, fileCount, results, uiUrl]
