openapi: "3.1.0"
info:
  title: LogisticsAI API
  version: "2.0.0"
  description: |
    Workspace-centric API for the LogisticsAI transport emissions evidence platform.

    The v2 surface exposes reporting workspaces, calculation runs, evidence packs,
    artifacts, approvals, evidence items, and webhook testing. Legacy v1 report
    endpoints remain available during migration and are documented as deprecated
    compatibility routes.

    Authentication: include an organisation-scoped API key as a Bearer token in the
    Authorization header.
  contact:
    email: enterprise@logisticsai.eu
  license:
    name: Proprietary
    url: https://logisticsai.eu/legal/terms

servers:
  - url: https://logisticsai.eu/api
    description: Production

security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: "lsai_<hex>"
      description: Organisation-scoped API key generated in LogisticsAI settings.

  schemas:
    PaginationMeta:
      type: object
      required: [page, per_page, total]
      properties:
        page:
          type: integer
          example: 1
        per_page:
          type: integer
          example: 20
        total:
          type: integer
          example: 42

    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string
          example: Unauthorized
        details:
          type: object
          additionalProperties: true

    Workspace:
      type: object
      required: [id, org_id, name, status, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
        org_id:
          type: string
          format: uuid
        name:
          type: string
        external_reference:
          type: [string, "null"]
        reporting_period_start:
          type: [string, "null"]
          format: date
        reporting_period_end:
          type: [string, "null"]
          format: date
        status:
          type: string
          enum: [draft, data_imported, calculated, review_needed, approved, published, archived]
        default_methodology_profile_id:
          type: [string, "null"]
          format: uuid
        latest_calculation_run_id:
          type: [string, "null"]
          format: uuid
        latest_report_pack_id:
          type: [string, "null"]
          format: uuid
        archived_at:
          type: [string, "null"]
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CalculationRun:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        org_id:
          type: string
          format: uuid
        methodology_profile_id:
          type: [string, "null"]
          format: uuid
        factor_version_id:
          type: [string, "null"]
          format: uuid
        legacy_report_id:
          type: [string, "null"]
          format: uuid
        status:
          type: string
          enum: [draft, running, completed, failed]
        rerun_hash:
          type: [string, "null"]
        assumptions_snapshot_json:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        completed_at:
          type: [string, "null"]
          format: date-time

    DataQualityAssessment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        calculation_run_id:
          type: string
          format: uuid
        report_pack_id:
          type: [string, "null"]
          format: uuid
        coverage_score:
          type: number
        primary_data_percent:
          type: number
        secondary_data_percent:
          type: number
        default_data_percent:
          type: number
        modelled_data_percent:
          type: number
        missing_required_fields_count:
          type: integer
        flagged_shipments_count:
          type: integer
        rules_triggered_json:
          type: array
          items:
            type: object
            additionalProperties: true
        summary_text:
          type: [string, "null"]
        assessed_at:
          type: string
          format: date-time

    ImportBatch:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        org_id:
          type: string
          format: uuid
        source_filename:
          type: [string, "null"]
        source_file_type:
          type: [string, "null"]
        source_row_count:
          type: integer
        mapping_profile_name:
          type: [string, "null"]
        source_system:
          type: [string, "null"]
        created_at:
          type: string
          format: date-time

    ReportPack:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        org_id:
          type: string
          format: uuid
        legacy_report_id:
          type: [string, "null"]
          format: uuid
        version_number:
          type: integer
        title:
          type: string
        audience_label:
          type: [string, "null"]
        status:
          type: string
          enum: [draft, generated, in_review, approved, published, archived, failed]
        methodology_profile_id:
          type: [string, "null"]
          format: uuid
        calculation_run_id:
          type: [string, "null"]
          format: uuid
        data_quality_assessment_id:
          type: [string, "null"]
          format: uuid
        export_template_id:
          type: [string, "null"]
          format: uuid
        pack_hash:
          type: [string, "null"]
        rerun_hash:
          type: [string, "null"]
        summary_pdf_url:
          type: [string, "null"]
        shipment_csv_url:
          type: [string, "null"]
        shipment_xlsx_url:
          type: [string, "null"]
        machine_json_url:
          type: [string, "null"]
        methodology_statement_url:
          type: [string, "null"]
        evidence_register_url:
          type: [string, "null"]
        quality_report_url:
          type: [string, "null"]
        audit_trail_url:
          type: [string, "null"]
        created_at:
          type: string
          format: date-time
        approved_at:
          type: [string, "null"]
          format: date-time
        published_at:
          type: [string, "null"]
          format: date-time

    PackArtifact:
      type: object
      properties:
        id:
          type: string
          format: uuid
        report_pack_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        artifact_type:
          type: string
          enum: [summary_pdf, shipment_csv, shipment_xlsx, machine_json, methodology_statement, evidence_register, quality_report, audit_trail]
        status:
          type: string
          enum: [pending, generated, failed]
        file_url:
          type: [string, "null"]
        file_name:
          type: [string, "null"]
        content_type:
          type: [string, "null"]
        created_at:
          type: string
          format: date-time
        generated_at:
          type: [string, "null"]
          format: date-time
        signed_download_url:
          type: [string, "null"]
          format: uri

    ApprovalActionResult:
      type: object
      properties:
        reportPackId:
          type: string
          format: uuid
        workspaceId:
          type: string
          format: uuid
        statusBefore:
          type: string
        statusAfter:
          type: string

    EvidenceItem:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspace_id:
          type: [string, "null"]
          format: uuid
        report_pack_id:
          type: [string, "null"]
          format: uuid
        shipment_id:
          type: [string, "null"]
          format: uuid
        org_id:
          type: string
          format: uuid
        evidence_type:
          type: string
          enum: [fuel_receipt, charging_invoice, telematics, other]
        title:
          type: string
        file_name:
          type: string
        file_url:
          type: string
        file_sha256:
          type: [string, "null"]
        mime_type:
          type: [string, "null"]
        issue_date:
          type: [string, "null"]
          format: date
        valid_from:
          type: [string, "null"]
          format: date
        valid_to:
          type: [string, "null"]
          format: date
        source_system:
          type: [string, "null"]
        verification_status:
          type: string
          enum: [unverified, verified, expired, rejected]
        attachment_scope:
          type: string
          enum: [organization, workspace, shipment]
        extracted_metadata_json:
          type: object
          additionalProperties: true
        notes:
          type: [string, "null"]
        uploaded_at:
          type: string
          format: date-time

    WebhookTestResult:
      type: object
      properties:
        delivered:
          type: boolean
        target_url:
          type: string
          format: uri
        event_type:
          type: string
        http_status:
          type: integer
        response_excerpt:
          type: string
        payload:
          type: object
          additionalProperties: true

paths:
  /v2/workspaces:
    get:
      summary: List Workspaces
      operationId: listWorkspaces
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Paginated workspaces
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Workspace"
                  pagination:
                    $ref: "#/components/schemas/PaginationMeta"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    post:
      summary: Create Workspace
      operationId: createWorkspace
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                external_reference:
                  type: string
                reporting_period_start:
                  type: string
                  format: date
                reporting_period_end:
                  type: string
                  format: date
                default_methodology_profile_id:
                  type: string
                  format: uuid
      responses:
        "201":
          description: Workspace created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Workspace"

  /v2/workspaces/{id}:
    get:
      summary: Get Workspace
      operationId: getWorkspace
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Workspace detail
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    allOf:
                      - $ref: "#/components/schemas/Workspace"
                      - type: object
                        properties:
                          latest_calculation_run:
                            $ref: "#/components/schemas/CalculationRun"
                          latest_pack:
                            $ref: "#/components/schemas/ReportPack"
                          latest_quality_assessment:
                            $ref: "#/components/schemas/DataQualityAssessment"
        "404":
          description: Workspace not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v2/workspaces/{id}/imports:
    post:
      summary: Register Import Batch
      operationId: createImportBatch
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [source_row_count]
              properties:
                source_filename:
                  type: string
                source_file_type:
                  type: string
                source_row_count:
                  type: integer
                mapping_profile_name:
                  type: string
                source_system:
                  type: string
      responses:
        "201":
          description: Import batch registered
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ImportBatch"

  /v2/workspaces/{id}/calculate:
    post:
      summary: Create Calculation Run
      operationId: createCalculationRun
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                legacy_report_id:
                  type: string
                  format: uuid
                export_template_id:
                  type: string
                  format: uuid
                  description: Optional evidence-pack template bundle to apply during generation
                assumptions_snapshot:
                  type: object
                  additionalProperties: true
      responses:
        "200":
          description: Calculation run created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      workspace_id:
                        type: string
                        format: uuid
                      status:
                        type: string
                      calculation_run:
                        $ref: "#/components/schemas/CalculationRun"
                      quality_assessment:
                        $ref: "#/components/schemas/DataQualityAssessment"
        "409":
          description: No compatible legacy report available
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v2/workspaces/{id}/quality:
    get:
      summary: Get Latest Workspace Quality
      operationId: getWorkspaceQuality
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Latest quality assessment
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/DataQualityAssessment"

  /v2/workspaces/{id}/packs:
    post:
      summary: Generate Evidence Pack
      operationId: createPack
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                legacy_report_id:
                  type: string
                  format: uuid
      responses:
        "201":
          description: Evidence pack generated or refreshed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReportPack"
                  meta:
                    type: object
                    additionalProperties: true

  /v2/packs/{id}:
    get:
      summary: Get Pack
      operationId: getPack
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Pack detail
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    allOf:
                      - $ref: "#/components/schemas/ReportPack"
                      - type: object
                        properties:
                          calculation_run:
                            $ref: "#/components/schemas/CalculationRun"
                          quality_assessment:
                            $ref: "#/components/schemas/DataQualityAssessment"
                          artifacts:
                            type: array
                            items:
                              $ref: "#/components/schemas/PackArtifact"

  /v2/packs/{id}/artifacts:
    get:
      summary: List Pack Artifacts
      operationId: listPackArtifacts
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Pack artifacts with signed URLs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PackArtifact"

  /v2/packs/{id}/approvals:
    post:
      summary: Record Approval Action
      operationId: createApprovalAction
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action_type]
              properties:
                action_type:
                  type: string
                  enum: [requested, approved, rejected, published]
                comment:
                  type: string
      responses:
        "201":
          description: Approval action recorded
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ApprovalActionResult"

  /v2/evidence-items:
    post:
      summary: Create Evidence Item
      operationId: createEvidenceItem
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [evidence_type, title, file_name, file_url]
              properties:
                workspace_id:
                  type: string
                  format: uuid
                report_pack_id:
                  type: string
                  format: uuid
                shipment_id:
                  type: string
                  format: uuid
                evidence_type:
                  type: string
                  enum: [fuel_receipt, charging_invoice, telematics, other]
                title:
                  type: string
                file_name:
                  type: string
                file_url:
                  type: string
                file_sha256:
                  type: string
                mime_type:
                  type: string
                issue_date:
                  type: string
                  format: date
                valid_from:
                  type: string
                  format: date
                valid_to:
                  type: string
                  format: date
                source_system:
                  type: string
                verification_status:
                  type: string
                  enum: [unverified, verified, expired, rejected]
                attachment_scope:
                  type: string
                  enum: [organization, workspace, shipment]
                extracted_metadata_json:
                  type: object
                  additionalProperties: true
                notes:
                  type: string
      responses:
        "201":
          description: Evidence item created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/EvidenceItem"

  /v2/webhooks/test:
    post:
      summary: Send Test Webhook
      operationId: testWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [target_url]
              properties:
                target_url:
                  type: string
                  format: uri
                event_type:
                  type: string
                workspace_id:
                  type: string
                  format: uuid
                report_pack_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Webhook test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/WebhookTestResult"
        "502":
          description: Webhook target did not accept the test request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/reports:
    get:
      deprecated: true
      summary: List Legacy Reports
      operationId: listReportsV1
      responses:
        "200":
          description: Legacy report list

  /v1/reports/{id}:
    get:
      deprecated: true
      summary: Get Legacy Report
      operationId: getReportV1
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Legacy report detail
