openapi: 3.0.3
info:
  title: Arctura Data Products API
  description: REST API for fleet telemetry, scores, and reports. Leasing and insurance use cases.
  version: 1.2.0
  contact:
    name: Arctura / NeurFlow
    url: https://neurflow.fi

servers:
  - url: https://neurflow.fi/api/v1
    description: Production
  - url: http://localhost:3000/api/v1
    description: Local development

security:
  - ApiKeyAuth: []

paths:
  /fleet:
    get:
      summary: Fleet Snapshot
      description: Returns current fleet_snapshot for tenant. Real-time vehicle state.
      operationId: getFleet
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 500
          description: Max rows to return (default 100)
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        device_id: { type: string }
                        device_name: { type: string }
                        vehicle_speed: { type: number }
                        battery_voltage: { type: number }
                        lat: { type: number }
                        lon: { type: number }
                        status: { type: string }
                        alert_level: { type: string }
                        weather_temp: { type: number }
                        ignition: { type: number }
                        updated_at: { type: string, format: date-time }
                  count: { type: integer }
                  tenant_id: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"

  /alerts:
    get:
      summary: Realtime Alerts
      description: Returns realtime_alerts for tenant. AI agent alerts.
      operationId: getAlerts
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 200
        - name: severity
          in: query
          schema:
            type: string
            enum: [CRITICAL, WARNING, INFO]
        - name: acknowledged
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: string }
                        device_id: { type: string }
                        agent: { type: string }
                        severity: { type: string }
                        prediction: { type: string }
                        confidence: { type: number }
                        technical_data: { type: object }
                        acknowledged: { type: boolean }
                        acknowledged_by: { type: string }
                        acknowledged_at: { type: string }
                        created_at: { type: string }
                  count: { type: integer }
                  tenant_id: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"

  /scores/fleet-risk:
    get:
      summary: Fleet Risk Score
      description: Aggregated fleet risk 0–100. EU AI Act compliant. Insurance use case.
      operationId: getFleetRisk
      parameters:
        - name: period
          in: query
          schema:
            type: string
            default: "30d"
            pattern: "^[0-9]+d$"
          description: Period, e.g. 7d, 30d, 90d
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  fleet_risk_score: { type: number }
                  score_version: { type: string }
                  confidence: { type: string }
                  period_days: { type: integer }
                  device_count: { type: integer }
                  alerts_period: { type: integer }
                  critical_alerts_period: { type: integer }
                  severity_distribution:
                    type: object
                    properties:
                      critical: { type: integer }
                      warning: { type: integer }
                      info: { type: integer }
                  scoring_method: { type: string }
                  components:
                    type: object
                    properties:
                      event_severity: { type: number }
                      weather_exposure: { type: number }
                      time_pattern: { type: number }
                      speed_compliance: { type: number }
                      ev_charging_risk: { type: number }
                  limitations: { type: array, items: { type: string } }
                  generated_at: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"

  /scores/vehicle-health/{device_id}:
    get:
      summary: Vehicle Health Score
      description: Per-vehicle health 0–100. EU AI Act compliant.
      operationId: getVehicleHealth
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  vehicle_health_score: { type: number }
                  score_version: { type: string }
                  confidence: { type: string }
                  components: { type: object }
                  available_signals: { type: array, items: { type: string } }
                  missing_signals: { type: array, items: { type: string } }
                  limitations: { type: array, items: { type: string } }
                  generated_at: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /scores/green-fleet:
    get:
      summary: Green Fleet Score
      description: Fleet-level environmental score (CSRD/ESG).
      operationId: getGreenFleet
      parameters:
        - name: period
          in: query
          schema:
            type: string
            default: "30d"
            pattern: "^[0-9]+d$"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  green_fleet_score: { type: number }
                  score_version: { type: string }
                  components: { type: object }
                  limitations: { type: array, items: { type: string } }
                  generated_at: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"

  /vehicles/{device_id}/battery-health-snapshot:
    get:
      summary: Battery Health Snapshot
      description: EV battery state. Leasing use case. No BMS SoH without OEM integration.
      operationId: getBatteryHealthSnapshot
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  snapshot:
                    type: object
                    properties:
                      soc_current_pct: { type: number }
                      battery_voltage: { type: number }
                      charging_status: { type: string }
                      charging_power_kw: { type: number }
                      estimated_range_km: { type: number }
                  last_battery_alert:
                    type: object
                    nullable: true
                    properties:
                      severity: { type: string }
                      prediction: { type: string }
                      confidence: { type: number }
                      created_at: { type: string }
                  data_quality: { type: object }
                  generated_at: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /vehicles/{device_id}/eol-summary:
    get:
      summary: EOL Summary
      description: End-of-Life summary for leasing. Condition grade A–F.
      operationId: getEolSummary
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  condition_grade: { type: string }
                  vehicle_health_score: { type: number }
                  limitations: { type: array, items: { type: string } }
                  generated_at: { type: string }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /reports/emissions:
    get:
      summary: CSRD Emissions Report
      description: Fleet emissions report (Haversine + DEFRA factors). Audit-ready.
      operationId: getEmissionsReport
      parameters:
        - name: start_date
          in: query
          required: true
          schema:
            type: string
            format: date
            example: "2026-01-01"
        - name: end_date
          in: query
          required: true
          schema:
            type: string
            format: date
            example: "2026-01-31"
        - name: format
          in: query
          schema:
            type: string
            enum: [json, csv]
            default: json
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  period_start: { type: string }
                  period_end: { type: string }
                  total_vehicles: { type: integer }
                  ev_count: { type: integer }
                  ice_count: { type: integer }
                  total_co2_kg_estimate: { type: number }
                  generated_at: { type: string }
        "400":
          description: Missing or invalid start_date/end_date
        "401":
          $ref: "#/components/responses/Unauthorized"

  /scores/batch:
    post:
      summary: Bulk Scoring
      description: |
        Batch endpoint for enterprise customers. Accepts up to 100 device_ids and returns
        all requested scores in a single call. Uses Promise.allSettled so individual device
        failures don't fail the batch.
      operationId: postScoresBatch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [device_ids]
              properties:
                device_ids:
                  type: array
                  items: { type: string }
                  minItems: 1
                  maxItems: 100
                  description: List of device IDs to score (max 100)
                scores:
                  type: array
                  items:
                    type: string
                    enum: [fleet_risk, vehicle_health, green_fleet, battery_health, eol_summary]
                  description: Optional subset of scores. Defaults to all.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        device_id: { type: string }
                        fleet_risk: { type: object }
                        vehicle_health: { type: object }
                        green_fleet: { type: object }
                        battery_health: { type: object }
                        eol_summary: { type: object }
                  meta:
                    type: object
                    properties:
                      total: { type: integer }
                      processed: { type: integer }
                      errors: { type: integer }
                      scores_requested:
                        type: array
                        items: { type: string }
                  generated_at: { type: string, format: date-time }
        "400":
          description: Invalid request (empty device_ids, over 100 limit, invalid scores)
        "401":
          $ref: "#/components/responses/Unauthorized"

  /vehicles/{device_id}/residual-value:
    get:
      summary: Residual Value Prediction
      description: |
        Rule-based residual value estimation for leasing. Combines fleet snapshot,
        vehicle scores, and mileage data to predict remaining asset value.
        estimation_method field enables future swap to ML model.
      operationId: getResidualValue
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  estimation_method: { type: string, example: "rule_based_v1" }
                  estimated_residual_pct: { type: number }
                  estimated_residual_eur: { type: number, nullable: true }
                  confidence: { type: string, enum: [low, medium, high] }
                  factors:
                    type: object
                    properties:
                      age_months: { type: integer, nullable: true }
                      mileage_km: { type: number }
                      battery_soh_pct: { type: number, nullable: true }
                      vehicle_health_score: { type: number, nullable: true }
                      fleet_risk_score: { type: number, nullable: true }
                      green_fleet_score: { type: number, nullable: true }
                      eol_grade: { type: string, nullable: true }
                  depreciation_curve:
                    type: object
                    properties:
                      method: { type: string }
                      annual_rate_pct: { type: number }
                      battery_penalty_pct: { type: number }
                      mileage_penalty_pct: { type: number }
                  data_quality:
                    type: object
                    properties:
                      completeness_pct: { type: number }
                      missing_fields:
                        type: array
                        items: { type: string }
                  disclaimer: { type: string }
                  generated_at: { type: string, format: date-time }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /certificates/generate/{device_id}:
    post:
      summary: Generate Health Certificate
      description: |
        Generate a Digital Vehicle Health Certificate for a specific device.
        Aggregates data from eol-summary, battery-health-snapshot, residual-value,
        and vehicle-health endpoints into a verifiable certificate with PDF.
      operationId: generateCertificate
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Certificate generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  certificate_number: { type: string, example: "Arctura-CERT-20260220-AB3KZ" }
                  device_id: { type: string }
                  grade: { type: string, enum: [A, B, C, D, F] }
                  vehicle_health_score: { type: number, nullable: true }
                  residual_value_pct: { type: number, nullable: true }
                  confidence: { type: string }
                  verify_url: { type: string }
                  verify_hash: { type: string }
                  generated_at: { type: string, format: date-time }
                  expires_at: { type: string, format: date-time }
                  pdf_base64: { type: string, nullable: true }
                  data_snapshot: { type: object }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /certificates/{certificate_id}:
    get:
      summary: Retrieve Certificate
      description: Retrieve a previously generated certificate by its certificate number.
      operationId: getCertificate
      parameters:
        - name: certificate_id
          in: path
          required: true
          schema:
            type: string
          description: Certificate number (e.g. Arctura-CERT-20260220-AB3KZ)
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  certificate_number: { type: string }
                  device_id: { type: string }
                  grade: { type: string }
                  vehicle_health_score: { type: number, nullable: true }
                  residual_value_pct: { type: number, nullable: true }
                  verify_hash: { type: string }
                  generated_at: { type: string, format: date-time }
                  expires_at: { type: string, format: date-time }
                  is_expired: { type: boolean }
                  data_snapshot: { type: object }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /reports/csrd/generate:
    post:
      summary: Generate CSRD Emissions Report
      description: |
        Generate a CSRD-aligned fleet emissions report (ESRS E1 disclosure).
        Internally aggregates data from /reports/emissions and /scores/green-fleet.
        Returns PDF by default; use ?format=json for raw data.
      operationId: generateCSRDReport
      parameters:
        - name: start_date
          in: query
          required: true
          schema: { type: string, format: date }
          description: Start of reporting period (YYYY-MM-DD)
        - name: end_date
          in: query
          required: true
          schema: { type: string, format: date }
          description: End of reporting period (YYYY-MM-DD)
        - name: format
          in: query
          required: false
          schema: { type: string, enum: [pdf, json], default: pdf }
          description: Response format (pdf or json)
      responses:
        "200":
          description: Report generated
          content:
            application/pdf:
              schema: { type: string, format: binary }
            application/json:
              schema:
                type: object
                properties:
                  period: { type: object }
                  fleet_summary: { type: object }
                  emissions: { type: object }
                  green_fleet_score: { type: object }
                  ev_metrics: { type: object }
                  data_quality: { type: object }
                  generated_at: { type: string, format: date-time }
        "400":
          description: Invalid date params
        "401":
          $ref: "#/components/responses/Unauthorized"

  /energy/spot-prices:
    get:
      summary: Finland Electricity Spot Prices
      description: |
        Public endpoint (no auth). Returns current Finnish electricity spot prices
        from Nord Pool via Porssisahko.net. Cached for 15 minutes.
      operationId: getSpotPrices
      security: []
      responses:
        "200":
          description: Spot prices
          content:
            application/json:
              schema:
                type: object
                properties:
                  prices:
                    type: array
                    items:
                      type: object
                      properties:
                        price_cents_kwh: { type: number, example: 3.45 }
                        start: { type: string, format: date-time }
                        end: { type: string, format: date-time }
                  cached: { type: boolean }
                  fetched_at: { type: string, format: date-time }
                  source: { type: string }
                  market: { type: string }

  /charging/recommendation/{device_id}:
    get:
      summary: Smart Charging Recommendation
      description: |
        SOC-based charging recommendation using Finnish spot prices.
        Finds cheapest charging window and computes cost savings.
      operationId: getChargingRecommendation
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Charging recommendation
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  recommendation: { type: string, enum: [charge_now, charge_later, no_charging_needed] }
                  soc_current_pct: { type: number }
                  soc_target_pct: { type: number }
                  energy_needed_kwh: { type: number }
                  cheapest_window:
                    type: object
                    nullable: true
                    properties:
                      start: { type: string, format: date-time }
                      end: { type: string, format: date-time }
                      avg_price_cents_kwh: { type: number }
                  current_price_cents_kwh: { type: number, nullable: true }
                  estimated_cost_cheapest_eur: { type: number, nullable: true }
                  estimated_cost_now_eur: { type: number, nullable: true }
                  savings_pct: { type: number, nullable: true }
                  charging_power_kw: { type: number }
                  reason: { type: string }
                  generated_at: { type: string, format: date-time }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          description: Device is not an EV (no SOC data)

  /oem/ingest:
    post:
      summary: OEM Bulk Ingest
      description: |
        Receives OEM vehicle telemetry from Frends iPaaS or direct OEM push.
        Validates payload and upserts to fleet_snapshot. Designed for
        Secto/Frends integration: OEM APIs → Frends (transform, sign) → Arctura.
        Sandbox mode returns mock acceptance response without storage.
      operationId: postOemIngest
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [vehicles]
              properties:
                vehicles:
                  type: array
                  items:
                    type: object
                    required: [vin]
                    properties:
                      vin: { type: string, description: "Vehicle Identification Number" }
                      device_id: { type: string, description: "Optional Arctura device ID. Defaults to oem-{vin}" }
                      timestamp: { type: string, format: date-time }
                      odometer_km: { type: number }
                      soc_percent: { type: number, description: "State of Charge %" }
                      soh_percent: { type: number, description: "State of Health % (from OEM BMS)" }
                      battery_voltage: { type: number }
                      charging_status: { type: string }
                      lat: { type: number }
                      lon: { type: number }
                      speed_kmh: { type: number }
                      fuel_level_pct: { type: number }
                      engine_hours: { type: number }
                      dtc_codes:
                        type: array
                        items: { type: string }
                        description: "Diagnostic Trouble Codes"
                      raw_data: { type: object, description: "Pass-through OEM-specific fields" }
                  minItems: 1
                  description: "Array of vehicle telemetry records"
                source: { type: string, description: "Source system identifier (e.g. frends, bmw, tesla)" }
                hmac_signature: { type: string, description: "HMAC-SHA256 payload signature for verification" }
      responses:
        "200":
          description: Accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted: { type: boolean, example: true }
                  device_count: { type: integer }
                  tenant_id: { type: string }
        "400":
          description: Invalid JSON body or empty vehicles array
        "401":
          $ref: "#/components/responses/Unauthorized"

  /tas/{device_id}:
    get:
      summary: Total Asset Score
      description: |
        Composite risk grade (A–F) for a single asset. Combines multiple pillar inputs
        (battery health, usage behavior, contractual/market risk, regulatory compliance)
        into one score. Configurable per asset class.
      operationId: getTas
      parameters:
        - $ref: "#/components/parameters/DeviceId"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  tas_grade: { type: string, enum: [A, B, C, D, E, F] }
                  tas_score: { type: number, description: "Numeric score 0-100" }
                  score_version: { type: string, example: "2.1.0" }
                  confidence: { type: string, enum: [high, medium, low] }
                  pillars:
                    type: object
                    description: "Pillar scores. Pillar weights are not exposed (proprietary)."
                    additionalProperties:
                      type: object
                      properties:
                        score: { type: number, description: "Pillar score 0-100" }
                        source: { type: string, description: "Data source identifier" }
                  knockout_flags:
                    type: array
                    description: "List of triggered knockout rule identifiers (empty if none triggered)"
                    items: { type: string }
                  vertical: { type: string, description: "Asset class identifier" }
                  fuel_type: { type: string, nullable: true }
                  generated_at: { type: string, format: date-time }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /tas/batch:
    post:
      summary: TAS Batch Scoring
      description: |
        Bulk TAS scoring for up to 1000 vehicles. Returns TAS grades for all requested device IDs.
        Uses Promise.allSettled — individual device failures don't fail the batch.
      operationId: postTasBatch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [device_ids]
              properties:
                device_ids:
                  type: array
                  items: { type: string }
                  minItems: 1
                  maxItems: 1000
                  description: List of device IDs to score (max 1000)
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        device_id: { type: string }
                        tas_grade: { type: string }
                        tas_score: { type: number }
                        confidence: { type: string }
                        error: { type: string, nullable: true }
                  meta:
                    type: object
                    properties:
                      total: { type: integer }
                      scored: { type: integer }
                      errors: { type: integer }
                      score_version: { type: string }
                  generated_at: { type: string, format: date-time }
        "400":
          description: Invalid request (empty device_ids or over 1000 limit)
        "401":
          $ref: "#/components/responses/Unauthorized"

  /predict/mileage/{device_id}:
    get:
      summary: Mileage Prediction
      description: |
        Km-overrun forecast. Predicts whether a vehicle will exceed
        its contracted mileage at lease end. Returns forecast, risk score, and confidence intervals.
      operationId: getPredictMileage
      parameters:
        - $ref: "#/components/parameters/DeviceId"
        - name: contract_km
          in: query
          schema: { type: integer }
          description: Contract mileage limit (km)
        - name: lease_months
          in: query
          schema: { type: integer }
          description: Total lease duration in months
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  current_km: { type: number }
                  predicted_end_km: { type: number }
                  contract_km: { type: integer, nullable: true }
                  overage_km: { type: number, nullable: true }
                  risk_score: { type: number, description: "Overrun risk 0-100" }
                  confidence_interval:
                    type: object
                    properties:
                      lower: { type: number }
                      upper: { type: number }
                  forecast_method: { type: string, example: "ml_v1" }
                  data_quality: { type: string, enum: [high, medium, low] }
                  generated_at: { type: string, format: date-time }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /predict/residual-value/{device_id}:
    get:
      summary: Residual Value Prediction
      description: |
        Residual value prediction for a vehicle, calibrated for Nordic markets.
        Returns estimated residual percentage, EUR value, and confidence.
        Optional explainability fields (subject to entitlement) describe the top
        contributing factors at a high level — model internals are not exposed.
      operationId: getPredictResidualValue
      parameters:
        - $ref: "#/components/parameters/DeviceId"
        - name: make
          in: query
          schema: { type: string }
          description: Vehicle make (e.g. Tesla, BMW, Volkswagen)
        - name: model
          in: query
          schema: { type: string }
          description: Vehicle model (e.g. Model 3, iX3)
        - name: year
          in: query
          schema: { type: integer }
          description: Model year
        - name: powertrain
          in: query
          schema: { type: string, enum: [BEV, PHEV], default: BEV }
          description: Powertrain type
        - name: age_months
          in: query
          schema: { type: integer }
          description: Vehicle age in months
        - name: odometer_km
          in: query
          schema: { type: number }
          description: Current odometer reading (km)
        - name: battery_soh_pct
          in: query
          schema: { type: number }
          description: Battery state of health (0-100)
        - name: battery_kwh
          in: query
          schema: { type: number }
          description: Battery capacity (kWh)
        - name: original_price_eur
          in: query
          schema: { type: number }
          description: Original purchase price (EUR)
        - name: electric_range_km
          in: query
          schema: { type: number }
          description: WLTP electric range (km)
        - name: power_kw
          in: query
          schema: { type: number }
          description: Motor power (kW)
        - name: mass_kg
          in: query
          schema: { type: number }
          description: Vehicle curb mass (kg)
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_id: { type: string }
                  estimated_residual_pct: { type: number, description: "Predicted residual value as % of original price" }
                  estimated_residual_eur: { type: number, nullable: true, description: "Absolute EUR value (if original_price_eur provided)" }
                  original_price_eur: { type: number, nullable: true }
                  confidence: { type: string, enum: [high, medium, low] }
                  data_quality: { type: string, enum: [high, medium, low] }
                  model_version: { type: string, example: "ml_v1" }
                  factors:
                    type: array
                    nullable: true
                    description: "Optional high-level contributing factors. Returned only for entitled tenants. Model internals (algorithm, training metrics, feature impacts) are not exposed."
                    items:
                      type: object
                      properties:
                        feature: { type: string, description: "High-level factor name (e.g. 'mileage', 'battery_health', 'segment')" }
                        direction: { type: string, enum: [positive, negative] }
                  generated_at: { type: string, format: date-time }
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: Bearer arctura_live_xxxx (API key from dashboard settings)

  parameters:
    DeviceId:
      name: device_id
      in: path
      required: true
      schema:
        type: string

  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            type: object
            properties:
              error: { type: string, example: "Unauthorized" }
              message: { type: string, example: "Valid API key required (Bearer arctura_live_...)" }
    NotFound:
      description: Device not found or not accessible for tenant
      content:
        application/json:
          schema:
            type: object
            properties:
              error: { type: string, example: "Not found" }
              message: { type: string }
