openapi: 3.1.0
info:
  title: Trusted AI Analyst Data API
  version: '1.0.0'
  description: >
    Data endpoints powering the analyst dashboard, portfolio-wide transaction review,
    company search, and per-company transaction drill-down experiences.
servers:
  - url: http://trusted_ai.test/api
    description: Local development (composer run dev)
  - url: https://afc.trai.sft.comstack.de/api
    description: Production
tags:
  - name: Dashboard
    description: KPIs and alert overviews for the landing dashboard.
  - name: Transactions
    description: Portfolio-wide transaction review tooling.
  - name: Companies
    description: Company discovery and drill-down data.
components:
  securitySchemes:
    sessionCookie:
      type: apiKey
      in: cookie
      name: laravel_session
      description: Browser session issued after login; required for all endpoints documented here.
  schemas:
    PaginationMeta:
      type: object
      required:
        - page
        - perPage
        - total
        - lastPage
      properties:
        page:
          type: integer
          minimum: 1
          example: 1
        perPage:
          type: integer
          minimum: 1
          example: 12
        total:
          type: integer
          minimum: 0
          example: 128
        lastPage:
          type: integer
          minimum: 1
          example: 11
    CountAmountSummary:
      type: object
      required:
        - count
        - amount
      properties:
        count:
          type: integer
          minimum: 0
          example: 42
        amount:
          type: number
          format: double
          example: 2750000.5
          description: Monetary volume in EUR unless noted otherwise.
    StatusOption:
      type: object
      required:
        - value
        - label
      properties:
        value:
          type: string
          example: true_positive
        label:
          type: string
          example: Bestätigte Treffer
    ChannelOption:
      type: object
      required:
        - value
        - label
      properties:
        value:
          type: string
          example: SEPA
        label:
          type: string
          example: SEPA
    RiskSegment:
      type: object
      required:
        - label
        - count
        - volume
      properties:
        label:
          type: string
          example: Kritisch (≥80)
        count:
          type: integer
          example: 17
        volume:
          type: number
          format: double
          example: 8900000.0
    TopCompanyAlert:
      type: object
      required:
        - companyId
        - companyName
        - ticker
        - sector
        - alerts
        - alertVolume
        - avgRiskScore
      properties:
        companyId:
          type: integer
          example: 24
        companyName:
          type: string
          example: Allianz SE
        ticker:
          type: string
          example: ALV
        sector:
          type: string
          example: Insurance
        alerts:
          type: integer
          example: 6
        alertVolume:
          type: number
          format: double
          example: 1450000.0
        avgRiskScore:
          type: integer
          minimum: 0
          maximum: 100
          example: 82
    DashboardOverview:
      type: object
      required:
        - generatedAt
        - activeAnalysts
        - totalTransactions
        - totalVolume
        - alertsToday
        - averageAlertsPerAnalyst
        - precisionRate
        - activeAlerts
        - riskSegments
        - topCompanies
        - runbooksExecutedToday
      properties:
        generatedAt:
          type: string
          format: date-time
          example: '2025-01-19T07:30:00Z'
        activeAnalysts:
          type: integer
          example: 24
        totalTransactions:
          type: integer
          example: 312
        totalVolume:
          type: number
          format: double
          example: 98765432.1
        alertsToday:
          type: integer
          example: 56
        averageAlertsPerAnalyst:
          type: integer
          example: 3
        precisionRate:
          type: integer
          minimum: 0
          maximum: 100
          example: 84
        activeAlerts:
          type: object
          required:
            - count
            - highRiskCount
          properties:
            count:
              type: integer
              example: 128
            highRiskCount:
              type: integer
              example: 19
        riskSegments:
          type: array
          items:
            $ref: '#/components/schemas/RiskSegment'
        topCompanies:
          type: array
          maxItems: 3
          items:
            $ref: '#/components/schemas/TopCompanyAlert'
        runbooksExecutedToday:
          type: integer
          example: 41
    TransactionStatus:
      type: string
      enum:
        - true_positive
        - false_positive
        - cleared
    CompanySummary:
      type: object
      required:
        - id
        - name
        - legalName
        - ticker
        - sector
        - country
        - headquarters
        - kycRiskLevel
        - summary
      properties:
        id:
          type: integer
          example: 12
        name:
          type: string
          example: Allianz
        legalName:
          type: string
          example: Allianz SE
        ticker:
          type: string
          example: ALV
        sector:
          type: string
          example: Insurance
        country:
          type: string
          example: Germany
        headquarters:
          type: string
          example: Munich
        kycRiskLevel:
          type: string
          example: high
        summary:
          type: string
          example: Multinational insurance provider with EMEA focus.
    CompanyDetail:
      allOf:
        - $ref: '#/components/schemas/CompanySummary'
        - type: object
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
            updatedAt:
              type: string
              format: date-time
              nullable: true
    TransactionPreview:
      type: object
      required:
        - id
        - company
        - counterparty
        - counterpartyCountry
        - status
        - statusLabel
        - requiresReview
        - riskScore
        - amount
        - currency
        - reference
        - channel
        - executedAt
      properties:
        id:
          type: integer
          example: 512
        company:
          $ref: '#/components/schemas/CompanySummary'
        counterparty:
          type: string
          example: Alpine Holdings Ltd.
        counterpartyCountry:
          type: string
          example: Switzerland
        status:
          $ref: '#/components/schemas/TransactionStatus'
        statusLabel:
          type: string
          example: Bestätigter Treffer
        requiresReview:
          type: boolean
          example: true
        riskScore:
          type: integer
          minimum: 0
          maximum: 100
          example: 87
        amount:
          type: number
          format: double
          example: 245000.75
        currency:
          type: string
          example: EUR
        reference:
          type: string
          example: PAY-2024-10-1942
        channel:
          type: string
          example: SWIFT
        executedAt:
          type: string
          format: date-time
          example: '2025-01-18T09:14:00Z'
        flaggedReason:
          type: string
          nullable: true
          example: Counterparty on sanctions watchlist
    TransactionSignal:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          example: Adverse Media
        value:
          type: string
          example: Enforcement action reported in 2024-11
        weight:
          type: number
          format: double
          nullable: true
          example: 0.8
    AlertSummary:
      type: object
      required:
        - id
        - counterparty
        - executedAt
        - channel
        - status
        - statusLabel
      properties:
        id:
          type: integer
          example: 911
        counterparty:
          type: string
          example: Northbridge Trading Ltd.
        executedAt:
          type: string
          format: date-time
          example: '2025-01-18T14:52:00Z'
        channel:
          type: string
          example: SWIFT
        status:
          $ref: '#/components/schemas/TransactionStatus'
        statusLabel:
          type: string
          example: Bestätigter Treffer
        riskScore:
          type: integer
          minimum: 0
          maximum: 100
          example: 92
    TransactionHistoryItem:
      type: object
      required:
        - id
        - executedAt
        - channel
        - reference
        - amount
        - currency
        - riskScore
      properties:
        id:
          type: integer
          example: 877
        executedAt:
          type: string
          format: date-time
          example: '2024-12-22T15:37:00Z'
        channel:
          type: string
          example: SWIFT
        reference:
          type: string
          example: PAY-2024-12-1187
        amount:
          type: number
          format: double
          example: 99000.0
        currency:
          type: string
          example: EUR
        riskScore:
          type: integer
          minimum: 0
          maximum: 100
          example: 74
    RecommendedAction:
      type: object
      required:
        - title
        - description
      properties:
        title:
          type: string
          example: Verdachtsmeldung vorbereiten
        description:
          type: string
          example: Erstellen Sie den Meldeentwurf für die FIU und sichern Sie Belege.
    TransactionDetail:
      allOf:
        - $ref: '#/components/schemas/TransactionPreview'
        - type: object
          required:
            - flaggedBy
            - flaggedReason
            - signals
          properties:
            flaggedBy:
              type: string
              example: Screening Engine
            flaggedReason:
              type: string
              example: Counterparty matched to EU sanctions list
            signals:
              type: array
              items:
                $ref: '#/components/schemas/TransactionSignal'
    TransactionCaseFile:
      allOf:
        - $ref: '#/components/schemas/TransactionDetail'
        - type: object
          properties:
            recommendedActions:
              type: array
              items:
                $ref: '#/components/schemas/RecommendedAction'
            counterpartyHistory:
              type: array
              items:
                $ref: '#/components/schemas/TransactionHistoryItem'
    TransactionMetrics:
      type: object
      required:
        - total
        - byStatus
      properties:
        total:
          $ref: '#/components/schemas/CountAmountSummary'
        byStatus:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/CountAmountSummary'
          description: Breakdown keyed by transaction status.
    CompanyTransactionMetrics:
      type: object
      required:
        - totalCount
        - totalVolume
        - openAlerts
        - highRiskShare
        - last30Days
        - byStatus
      properties:
        totalCount:
          type: integer
          example: 96
        totalVolume:
          type: number
          format: double
          example: 7200000.0
        openAlerts:
          $ref: '#/components/schemas/CountAmountSummary'
        highRiskShare:
          type: integer
          minimum: 0
          maximum: 100
          example: 28
        last30Days:
          $ref: '#/components/schemas/CountAmountSummary'
        byStatus:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/CountAmountSummary'
    TransactionCollectionResponse:
      type: object
      required:
        - filters
        - metrics
        - data
        - pagination
      properties:
        filters:
          type: object
          properties:
            statusOptions:
              type: array
              items:
                $ref: '#/components/schemas/StatusOption'
            perPage:
              type: integer
              example: 12
        metrics:
          $ref: '#/components/schemas/TransactionMetrics'
        data:
          type: array
          items:
            $ref: '#/components/schemas/TransactionPreview'
        pagination:
          $ref: '#/components/schemas/PaginationMeta'
        selectedTransactionId:
          type: integer
          nullable: true
          example: 512
        selectedTransaction:
          allOf:
            - $ref: '#/components/schemas/TransactionDetail'
          nullable: true
    CompanySearchOverview:
      type: object
      required:
        - totalCompanies
        - openAlerts
        - openAlertVolume
        - averageRiskScore
        - watchlistHits
        - automationShare
      properties:
        totalCompanies:
          type: integer
          example: 40
        openAlerts:
          type: integer
          example: 112
        openAlertVolume:
          type: number
          format: double
          example: 5640000.0
        averageRiskScore:
          type: integer
          minimum: 0
          maximum: 100
          example: 71
        watchlistHits:
          type: integer
          example: 9
        automationShare:
          type: integer
          minimum: 0
          maximum: 100
          example: 82
    CompanyAlertPreview:
      type: object
      required:
        - transactionId
        - counterparty
        - executedAt
        - channel
        - flaggedReason
      properties:
        transactionId:
          type: integer
          example: 731
        counterparty:
          type: string
          example: Baltic Commodities LLC
        executedAt:
          type: string
          format: date-time
          example: '2025-01-17T11:48:00Z'
        channel:
          type: string
          example: SWIFT
        flaggedReason:
          type: string
          example: Pattern matches sanctions typology
        riskScore:
          type: integer
          minimum: 0
          maximum: 100
          example: 88
    CompanySearchResult:
      allOf:
        - $ref: '#/components/schemas/CompanySummary'
        - type: object
          required:
            - alertCount
            - alertVolume
            - alertRiskScore
            - latestAlert
          properties:
            alertCount:
              type: integer
              example: 7
            alertVolume:
              type: number
              format: double
              example: 980000.0
            alertRiskScore:
              type: integer
              minimum: 0
              maximum: 100
              example: 79
            latestAlert:
              allOf:
                - $ref: '#/components/schemas/CompanyAlertPreview'
    CompanySearchResponse:
      type: object
      required:
        - overview
        - results
        - meta
      properties:
        overview:
          $ref: '#/components/schemas/CompanySearchOverview'
        results:
          type: array
          items:
            $ref: '#/components/schemas/CompanySearchResult'
        meta:
          type: object
          required:
            - query
            - resultCount
            - limit
          properties:
            query:
              type: string
              nullable: true
              example: Allianz
            resultCount:
              type: integer
              example: 6
            limit:
              type: integer
              example: 6
    CompanyTransactionsResponse:
      type: object
      required:
        - company
        - filters
        - metrics
        - data
        - pagination
      properties:
        company:
          $ref: '#/components/schemas/CompanyDetail'
        filters:
          type: object
          properties:
            statusOptions:
              type: array
              items:
                $ref: '#/components/schemas/StatusOption'
            channelOptions:
              type: array
              items:
                $ref: '#/components/schemas/ChannelOption'
        metrics:
          $ref: '#/components/schemas/CompanyTransactionMetrics'
        data:
          type: array
          items:
            $ref: '#/components/schemas/TransactionPreview'
        pagination:
          $ref: '#/components/schemas/PaginationMeta'
        selectedTransactionId:
          type: integer
          nullable: true
          example: 877
        selectedTransaction:
          allOf:
            - $ref: '#/components/schemas/TransactionCaseFile'
          nullable: true
        recentAlerts:
          type: array
          items:
            $ref: '#/components/schemas/AlertSummary'
paths:
  /dashboard/overview:
    get:
      tags:
        - Dashboard
      operationId: getDashboardOverview
      summary: Retrieve dashboard overview metrics
      description: >
        Aggregated KPIs and alert slices needed for the dashboard hero cards and side panels.
      security:
        - sessionCookie: []
      responses:
        '200':
          description: Overview data ready for dashboard rendering.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DashboardOverview'
        '401':
          description: Session invalid or expired.
  /transactions:
    get:
      tags:
        - Transactions
      operationId: listTransactions
      summary: List transactions with global filters
      description: >
        Returns the paginated transaction stream together with per-status metrics and default selection,
        mirroring the Livewire transaction review experience.
      security:
        - sessionCookie: []
      parameters:
        - name: search
          in: query
          schema:
            type: string
          description: Match against reference, counterparty, or company identifiers.
        - name: status
          in: query
          schema:
            type: string
            enum:
              - all
              - true_positive
              - false_positive
              - cleared
            default: all
          description: Filter by review status; `all` keeps every status.
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: perPage
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 12
      responses:
        '200':
          description: Paginated transaction set with metrics.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionCollectionResponse'
        '401':
          description: Session invalid or expired.
  /transactions/{transactionId}:
    get:
      tags:
        - Transactions
      operationId: getTransactionCaseFile
      summary: Fetch a single transaction case file
      description: >
        Detailed transaction record used when an analyst expands a case in the transaction review workspace.
      security:
        - sessionCookie: []
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Transaction detail with detection signals.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionDetail'
        '401':
          description: Session invalid or expired.
        '404':
          description: Transaction not found.
  /companies/search:
    get:
      tags:
        - Companies
      operationId: searchCompanies
      summary: Search companies and retrieve screening overview
      description: >
        Provides the metrics and top results required to populate the company search tiles.
      security:
        - sessionCookie: []
      parameters:
        - name: q
          in: query
          schema:
            type: string
          description: Free-text query across name, legal name, ticker, or sector.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 25
            default: 6
      responses:
        '200':
          description: Matching companies with alert context.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanySearchResponse'
        '401':
          description: Session invalid or expired.
  /companies/{companyId}:
    get:
      tags:
        - Companies
      operationId: getCompany
      summary: Retrieve a company profile
      description: >
        Returns core firmographics and KYC risk tier needed for per-company transaction pages.
      security:
        - sessionCookie: []
      parameters:
        - name: companyId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Company detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyDetail'
        '401':
          description: Session invalid or expired.
        '404':
          description: Company not found.
  /companies/{companyId}/transactions:
    get:
      tags:
        - Companies
      operationId: listCompanyTransactions
      summary: List transactions for a specific company
      description: >
        Supplies per-company metrics, channel/status filters, paginated cases, and the current selection
        for the company drill-down view.
      security:
        - sessionCookie: []
      parameters:
        - name: companyId
          in: path
          required: true
          schema:
            type: integer
        - name: status
          in: query
          schema:
            type: string
            enum:
              - all
              - true_positive
              - false_positive
              - cleared
            default: all
        - name: channel
          in: query
          schema:
            type: string
            default: all
          description: Channel label returned in channelOptions; use `all` for no filtering.
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: perPage
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
      responses:
        '200':
          description: Company-specific transaction data with metrics.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyTransactionsResponse'
        '401':
          description: Session invalid or expired.
        '404':
          description: Company not found or no transactions.
  /companies/{companyId}/transactions/{transactionId}:
    get:
      tags:
        - Companies
      operationId: getCompanyTransactionCaseFile
      summary: Fetch a company transaction case file with history
      description: >
        Returns the selected transaction enriched with recommended actions and counterparty history
        used in the right-hand panel of the company drill-down.
      security:
        - sessionCookie: []
      parameters:
        - name: companyId
          in: path
          required: true
          schema:
            type: integer
        - name: transactionId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Transaction case file for the company context.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionCaseFile'
        '401':
          description: Session invalid or expired.
        '404':
          description: Transaction not found for the given company.
