openapi: 3.1.0

info:
  title: Trakt System Integrations API
  version: "1.2.1"
  summary: Predictive maintenance data and forecasts for your fleet.
  description: |
    The Trakt Integrations API gives a partner programmatic access to the
    predictions Trakt produces for their fleet: per-component health and
    remaining useful life, a forward-looking maintenance forecast, and a fleet
    export.

    Authentication
    --------------
    Every request carries a bearer token in the `Authorization` header. A token
    is scoped to a company, so a request only ever returns that company's data.

        Authorization: Bearer YOUR_TOKEN

    Access levels
    -------------
    A token is issued at one of three access levels, which control both the
    endpoints it may reach and how many records a single request may return:

      * `read_only`  read predictions and forecasts
      * `read_write` read, plus the write endpoints (scenarios, configurations)
      * `full`       everything, including fleet export

    A request to an endpoint outside the token's level returns `403`.

    Limits
    ------
    Tokens carry a daily and a monthly request quota and a maximum number of
    records per request (`max_predictions`). Exceeding a quota returns `429`.
    Clients should retry a `429` with backoff; the SDKs do this automatically.
  contact:
    name: Kquika
    email: support@kquika.com
    url: https://www.trakt.tech/api-documentation

servers:
  - url: https://www.trakt.tech
    description: Production

security:
  - bearerAuth: []

tags:
  - name: Predictions
    description: Component-level predictions and the maintenance forecast.
  - name: Fleet
    description: Aircraft and fleet-level data.
  - name: Planning
    description: Planner grid, forecast, and prediction conversion.
  - name: Configuration
    description: Integration config, scenarios, and asset configurations.
  - name: Ingestion
    description: Batch ingestion of telemetry and source-system records.
  - name: Service
    description: Health, token introspection, and integration status.

paths:

  /api/integrations/config:
    get:
      tags: [Configuration]
      operationId: getConfig
      summary: Integration configuration for the calling token.
      description: |
        Returns what this token is permitted to do: its access level, the
        endpoints it may reach, and its record limits. Useful as a first call to
        confirm a token works and to discover its scope.
      responses:
        "200":
          description: Configuration for the calling token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigResponse"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/components/extended:
    get:
      tags: [Predictions]
      operationId: listComponents
      summary: Per-component predictions for the fleet.
      description: |
        Returns one entry per component with its current health, predicted
        remaining useful life, failure probability, recommended action, and
        survival analysis. This is the primary prediction feed.

        The number of records returned is capped by the token's
        `max_predictions` limit.
      parameters:
        - name: aircraft_id
          in: query
          description: Restrict to a single aircraft.
          schema: { type: integer }
        - name: component_type
          in: query
          description: Restrict to one component type (for example an ATA chapter).
          schema: { type: string }
        - name: priority
          in: query
          description: Restrict to one urgency band.
          schema:
            type: string
            enum: [critical, high, medium, low]
        - name: currency
          in: query
          description: Currency for cost fields.
          schema: { type: string, default: USD }
      responses:
        "200":
          description: Component predictions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ComponentsResponse"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/planner/forecast:
    get:
      tags: [Planning, Predictions]
      operationId: getForecast
      summary: Upcoming maintenance forecast.
      description: |
        Returns the items coming due within a window, ordered so the most urgent
        appear first. Each item carries the aircraft, the component, an urgency
        band, days until due (negative when already past due), and a suggested
        action date.
      parameters:
        - name: start_date
          in: query
          description: First day of the window. Defaults to today.
          schema: { type: string, format: date }
        - name: days
          in: query
          description: Window length in days.
          schema: { type: integer, minimum: 1, maximum: 90, default: 30 }
        - name: max_items
          in: query
          description: |
            Maximum items to return. A token is capped at 500; a browser session
            may request up to 2000.
          schema: { type: integer, minimum: 1, maximum: 500, default: 100 }
        - name: fleet_type
          in: query
          description: Restrict to one aircraft type (for example A320).
          schema: { type: string }
        - name: station
          in: query
          description: Restrict to one station.
          schema: { type: string }
        - name: fp_threshold
          in: query
          description: Only include items at or above this failure probability.
          schema: { type: number, format: float, minimum: 0, maximum: 1 }
      responses:
        "200":
          description: Forecast items for the window.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ForecastResponse"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/predictions/failure-windows:
    get:
      tags: [Predictions, Planning]
      operationId: getFailureWindows
      summary: Failure windows and the bucketed probability distribution.
      description: |
        The surface a scheduler consumes. For each component this returns the
        windows to schedule against and the probability distribution behind them.

        The **rectangular** window is p10 to p90: maintenance on any day inside it
        covers the prediction. Binary, in or out.

        The **triangular** window is p25 / median / p75: coverage quality is graded
        by how close a scheduled date sits to the center, so scheduling on the peak
        scores higher than scheduling at the edge.

        The **buckets** break failure probability into bands across the horizon.
        `failure_probability` is the marginal chance of failing inside that band.
        `conditional_failure_probability` is the chance of failing in it given
        survival to its start, which is usually what an optimizer wants for a
        cost-of-delay term.

        Remaining life is measured in FLIGHT hours, and a calendar day contains far
        fewer than 24 of them, so the conversion to dates depends on utilization.
        Each aircraft's own rate is derived where the history allows. Supply
        `utilization_hours_per_day` to override it.
      parameters:
        - name: component_ids
          in: query
          description: Comma-separated component ids. Omit for the whole fleet.
          schema: { type: string }
        - name: horizon_days
          in: query
          description: Planning horizon in days.
          schema: { type: integer, minimum: 1, maximum: 1460, default: 730 }
        - name: bucket_days
          in: query
          description: Width of each probability band, in days.
          schema: { type: integer, minimum: 1, default: 30 }
        - name: include_window
          in: query
          description: Set false to return the buckets alone, without the windows.
          schema: { type: boolean, default: true }
        - name: utilization_hours_per_day
          in: query
          description: Override the flight-hours-per-day conversion.
          schema: { type: number, format: float }
      responses:
        "200":
          description: Windows and bucketed probabilities per component.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FailureWindowsResponse" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/predictions/failure-instances:
    get:
      tags: [Predictions, Planning]
      operationId: getFailureInstances
      summary: Generate concrete failure instances for perturbation analysis.
      description: |
        An optimizer that only ever sees the expected timeline produces a plan that
        is optimal in a world where nothing goes wrong. To measure how a schedule
        degrades under unscheduled events, the optimizer needs realizations: specific
        components, on specific aircraft, failing on specific days. This generates
        them.

        Give it a target rate of unscheduled events and it returns that many
        independent instances, each a concrete list of failure events.

        **How the rate becomes failure days.** Each component's natural probability
        of failing inside the horizon is read from its own distribution. Those
        probabilities are scaled by one shared multiplier so the expected event count
        matches the rate requested, which preserves the relative risk between
        components, so a degraded part stays proportionally more likely to be drawn
        than a healthy one. A drawn component has its failure day sampled from its own
        curve, conditioned on failing inside the horizon.

        Every response carries the calibration it applied, so the sampling is
        auditable. Every instance is seeded, so a run reproduces exactly.

        **One behavior to know.** Over a long horizon a badly degraded part and a
        moderately degraded one are both close to certain to fail, so selection
        saturates. What separates them is *when*. The failure date carries the
        information; lean on it rather than on selection probability.
      parameters:
        - name: unscheduled_pct
          in: query
          description: Target unscheduled events, as a percent of components.
          schema: { type: number, format: float, minimum: 0, maximum: 100, default: 5 }
        - name: instances
          in: query
          description: Independent realizations to return.
          schema: { type: integer, minimum: 1, maximum: 25, default: 5 }
        - name: horizon_days
          in: query
          description: Scenario duration in days.
          schema: { type: integer, minimum: 1, maximum: 1460, default: 730 }
        - name: seed
          in: query
          description: Integer seed. Pass the same seed and parameters to reproduce a run exactly.
          schema: { type: integer }
        - name: aircraft_ids
          in: query
          description: Comma-separated aircraft ids to restrict the scenario to.
          schema: { type: string }
        - name: utilization_hours_per_day
          in: query
          description: Override the flight-hours-per-day conversion.
          schema: { type: number, format: float }
      responses:
        "200":
          description: Scenario instances and the calibration that produced them.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FailureInstancesResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/optimizer/feedback:
    post:
      tags: [Planning]
      operationId: submitOptimizerFeedback
      summary: Send optimization results back to Trakt.
      description: |
        This closes the loop. Trakt scores each scheduled slot against the failure
        window it originally sent you, records the coverage, and feeds the outcome
        into model retraining.

        Without it the windows stay static. They get richer, and they do not get
        better, because nothing tells us whether a window was well calibrated, whether
        the work was performed inside it, or whether the component failed when we said
        it would.

        Requires a `read_write` or `full` token. A `read_only` token is refused.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/OptimizerFeedback" }
      responses:
        "200":
          description: Feedback ingested, coverage scored, retraining queued.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  ingested: { type: integer }
                  coverage: { type: object, additionalProperties: true }
                  retraining_queued: { type: boolean }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/aircraft/extended:
    get:
      tags: [Fleet]
      operationId: listAircraft
      summary: Aircraft in the fleet, with fleet-level detail.
      responses:
        "200":
          description: Aircraft list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  total: { type: integer }
                  aircraft:
                    type: array
                    items: { $ref: "#/components/schemas/Aircraft" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/fleet/export:
    get:
      tags: [Fleet]
      operationId: exportFleet
      summary: Full fleet snapshot.
      description: |
        A combined export of the fleet. Requires a token whose access level
        permits export; other levels receive `403`.
      responses:
        "200":
          description: Fleet snapshot.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object, additionalProperties: true }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/fleet/summary:
    get:
      tags: [Fleet]
      operationId: getFleetSummary
      summary: Fleet-level rollup of health and confidence.
      description: |
        A fleet-level summary matching the Trakt dashboards: overall health,
        component counts, the composite average confidence (per-component
        confidence weighted by prediction coverage and recency, then blended
        with fleet failure risk), prediction coverage, and 30-day survival.
        Available at every access level. Scoped to the token's company.
      responses:
        "200":
          description: Fleet summary.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FleetSummaryResponse"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/planner/grid:
    get:
      tags: [Planning]
      operationId: getPlannerGrid
      summary: Planner grid for scheduling.
      parameters:
        - name: days
          in: query
          schema: { type: integer, minimum: 1, maximum: 90, default: 30 }
      responses:
        "200":
          description: Planner grid.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  grid: { type: array, items: { type: object, additionalProperties: true } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/scenarios:
    get:
      tags: [Configuration]
      operationId: listScenarios
      summary: Planning scenarios.
      responses:
        "200":
          description: Scenarios.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  scenarios: { type: array, items: { $ref: "#/components/schemas/Scenario" } }
        "401": { $ref: "#/components/responses/Unauthorized" }
    post:
      tags: [Configuration]
      operationId: createScenario
      summary: Create a planning scenario.
      description: Requires a token at `read_write` or `full`; `read_only` receives `403`.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ScenarioCreate" }
      responses:
        "200":
          description: The created scenario.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  scenario: { $ref: "#/components/schemas/Scenario" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/scenarios/{scenario_id}:
    get:
      tags: [Configuration]
      operationId: getScenario
      summary: A single planning scenario.
      parameters:
        - name: scenario_id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: The scenario.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  scenario: { $ref: "#/components/schemas/Scenario" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }

  /api/health-check:
    get:
      tags: [Service]
      operationId: healthCheck
      summary: Service status and this token's remaining quota.
      description: |
        The first call to make when wiring up a token. Confirms the service is
        reachable, that the credential authenticated, and how much of the daily
        and monthly quota is left, so a client can verify all three before
        touching a data endpoint.

        Available at every access level. When the database probe fails the
        response is still `200` with `status` set to `degraded`, because a
        health check that errors tells a caller nothing.
      responses:
        "200":
          description: Service status and token entitlements.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthCheckResponse"
        "401": { $ref: "#/components/responses/Unauthorized" }

  /api/token/info:
    get:
      tags: [Service]
      operationId: getTokenInfo
      summary: Details of the calling token.
      description: |
        Name, organization, access level, expiry, and current usage against the
        daily and monthly quotas. Available at every access level.
      responses:
        "200":
          description: Token details.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  token_info: { $ref: "#/components/schemas/TokenInfo" }
        "401": { $ref: "#/components/responses/Unauthorized" }

  /api/integrations/status:
    get:
      tags: [Service]
      operationId: getIntegrationStatus
      summary: Connection health of your configured integrations.
      description: |
        Connection health and last sync time for the MRO and data integrations
        configured for your company. Scoped to the token's company: a token only
        ever sees its own company's integrations.
      responses:
        "200":
          description: Integration status.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  integrations:
                    type: array
                    items: { type: object, additionalProperties: true }
        "401": { $ref: "#/components/responses/Unauthorized" }

  /api/integrations/predictions/available:
    get:
      tags: [Predictions]
      operationId: listAvailablePredictions
      summary: Components that currently carry a prediction.
      description: |
        Components with a prediction available, including the full model output.
        Capped at the token's `max_predictions`.
      parameters:
        - name: aircraft_id
          in: query
          schema: { type: integer }
        - name: component_type
          in: query
          schema: { type: string }
      responses:
        "200":
          description: Available predictions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  total: { type: integer }
                  components:
                    type: array
                    items: { $ref: "#/components/schemas/Component" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/integrations/predictions/probability-between:
    get:
      tags: [Predictions]
      operationId: getProbabilityBetween
      summary: Failure probability inside a given interval.
      description: |
        The probability mass that a component fails between two dates, read
        straight off the same failure-time distribution that backs the window
        and instance endpoints.

        Exists so an integrator scoring window coverage never has to
        reimplement the survival function and then drift from ours when we
        recalibrate.
      parameters:
        - name: component_id
          in: query
          required: true
          schema: { type: integer }
        - name: start
          in: query
          required: true
          description: Inclusive start of the interval, as a date.
          schema: { type: string, format: date }
        - name: end
          in: query
          required: true
          description: Inclusive end of the interval, as a date.
          schema: { type: string, format: date }
      responses:
        "200":
          description: Probability mass for the interval.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  component_id: { type: integer }
                  start: { type: string, format: date }
                  end: { type: string, format: date }
                  probability:
                    type: number
                    description: 0 to 1, the modeled chance of failure inside the interval.
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }

  /api/integrations/predictions/select:
    post:
      tags: [Predictions]
      operationId: selectPredictions
      summary: Predictions for a named set of components.
      description: |
        Legacy endpoint. Select specific components and prediction types.
        Failure window objects are returned alongside each prediction. Requires
        `read_write` or `full`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                component_ids:
                  type: array
                  items: { type: integer }
                prediction_types:
                  type: array
                  items: { type: string }
      responses:
        "200":
          description: Selected predictions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  predictions:
                    type: array
                    items: { type: object, additionalProperties: true }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/predictions/export:
    post:
      tags: [Predictions]
      operationId: exportPredictions
      summary: Export predictions as JSON or CSV.
      description: |
        Legacy endpoint. Each record includes its failure window objects.
        Requires a token whose access level permits export.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                format:
                  type: string
                  enum: [json, csv]
                  default: json
                component_ids:
                  type: array
                  items: { type: integer }
      responses:
        "200":
          description: Exported predictions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object, additionalProperties: true }
            text/csv:
              schema: { type: string }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/planner/convert-prediction:
    post:
      tags: [Planning]
      operationId: convertPrediction
      summary: Turn a predicted failure into a work order.
      description: |
        Creates a real work order from a prediction. When `work_type`,
        `priority`, and duration are omitted they are inferred from the
        component's signals. Requires `read_write` or `full`.

        Not idempotent: each call creates a work order.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [component_id]
              properties:
                component_id: { type: integer }
                scheduled_date: { type: string, format: date }
                work_type: { type: string }
                priority: { type: string }
                estimated_hours: { type: number }
      responses:
        "200":
          description: The created work order.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  work_order_id: { type: integer }
                  work_type: { type: string }
                  priority: { type: string }
                  estimated_hours: { type: number }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/asset-configurations:
    get:
      tags: [Configuration]
      operationId: listAssetConfigurations
      summary: Asset configurations, meaning aircraft types.
      description: |
        Aircraft types with their maintenance programs, specifications, and
        typical utilization.
      responses:
        "200":
          description: Asset configurations.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  asset_configurations:
                    type: array
                    items: { type: object, additionalProperties: true }
        "401": { $ref: "#/components/responses/Unauthorized" }
    post:
      tags: [Configuration]
      operationId: createAssetConfiguration
      summary: Create an asset configuration.
      description: |
        Creates an aircraft type and optionally attaches maintenance programs in
        the same request.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                manufacturer: { type: string }
                model: { type: string }
                typical_utilization_hours_per_day: { type: number }
                maintenance_programs:
                  type: array
                  items: { type: object, additionalProperties: true }
      responses:
        "200":
          description: The created asset configuration.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  asset_configuration: { type: object, additionalProperties: true }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/integrations/maintenance-programs:
    get:
      tags: [Configuration]
      operationId: listMaintenancePrograms
      summary: Maintenance programs and their intervals.
      description: |
        Programs supporting calendar, flight-hour, flight-cycle, and combined
        intervals.
      parameters:
        - name: interval_type
          in: query
          schema:
            type: string
            enum: [calendar, flight_hours, flight_cycles, combined]
      responses:
        "200":
          description: Maintenance programs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  maintenance_programs:
                    type: array
                    items: { type: object, additionalProperties: true }
        "401": { $ref: "#/components/responses/Unauthorized" }

  /api/v1/sensor-data/batch:
    post:
      tags: [Ingestion]
      operationId: ingestSensorDataBatch
      summary: Batch ingestion for sensor telemetry.
      description: |
        Machine-to-machine ingestion for flight-data recorders, IoT hubs, and
        MRO systems. Up to 1000 readings per request.

        Every row's `component_id` is verified against the token's company, so a
        cross-tenant row is rejected on its own while the rest of the batch
        still lands. Supplying `external_id` makes retries idempotent: a repeat
        of the same identifier at the same timestamp is reported as a duplicate
        rather than stored twice.

        Requires `read_write` or `full`; a `read_only` token is refused.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [readings]
              properties:
                readings:
                  type: array
                  maxItems: 1000
                  items: { $ref: "#/components/schemas/SensorReading" }
      responses:
        "200":
          description: Batch processed. Inspect the counts and errors.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BatchIngestResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/v1/aircraft/batch:
    post:
      tags: [Ingestion]
      operationId: ingestAircraftBatch
      summary: Batch ingestion for aircraft records.
      description: |
        Upserts by `external_system` plus `external_id`, so re-sending a record
        updates it rather than creating a second copy. Tenant-scoped to the
        token's company.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [aircraft]
              properties:
                aircraft:
                  type: array
                  items: { type: object, additionalProperties: true }
      responses:
        "200":
          description: Batch processed.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BatchIngestResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/v1/components/batch:
    post:
      tags: [Ingestion]
      operationId: ingestComponentsBatch
      summary: Batch ingestion for component records.
      description: |
        Upserts by `external_system` plus `external_id`. Tenant-scoped to the
        token's company.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [components]
              properties:
                components:
                  type: array
                  items: { type: object, additionalProperties: true }
      responses:
        "200":
          description: Batch processed.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BatchIngestResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/v1/maintenance/batch:
    post:
      tags: [Ingestion]
      operationId: ingestMaintenanceBatch
      summary: Batch ingestion for maintenance history.
      description: Tenant-scoped to the token's company.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [maintenance]
              properties:
                maintenance:
                  type: array
                  items: { type: object, additionalProperties: true }
      responses:
        "200":
          description: Batch processed.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BatchIngestResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /api/v1/work-orders/batch:
    post:
      tags: [Ingestion]
      operationId: ingestWorkOrdersBatch
      summary: Batch ingestion for work orders.
      description: Tenant-scoped to the token's company.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [work_orders]
              properties:
                work_orders:
                  type: array
                  items: { type: object, additionalProperties: true }
      responses:
        "200":
          description: Batch processed.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BatchIngestResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

components:

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        The API token issued to your organization. Send it as
        `Authorization: Bearer YOUR_TOKEN`. Treat it like a password.

  responses:
    BadRequest:
      description: The request was malformed, or a required value was missing.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Unauthorized:
      description: The token is missing, malformed, revoked, or expired.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Forbidden:
      description: The token is valid but its access level does not permit this endpoint.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: The resource does not exist, or is not visible to this token.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    RateLimited:
      description: A daily or monthly quota for this token has been exceeded.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }

  schemas:

    RulDistribution:
      type: object
      description: The uncertainty around a remaining-life estimate.
      properties:
        p10_date:    { type: [string, "null"], format: date }
        median_date: { type: [string, "null"], format: date }
        p90_date:    { type: [string, "null"], format: date }
        p10_days:    { type: [number, "null"] }
        median_days: { type: [number, "null"] }
        p90_days:    { type: [number, "null"] }
        utilization_hours_per_day:
          type: [number, "null"]
          description: Flight hours per calendar day used to convert life into dates.

    RectangularWindow:
      type: object
      description: |
        p10 to p90. Maintenance scheduled on any day inside this range covers the
        prediction. Binary coverage.
      properties:
        start_date:   { type: [string, "null"], format: date }
        end_date:     { type: [string, "null"], format: date }
        start_hours:  { type: [number, "null"] }
        end_hours:    { type: [number, "null"] }
        coverage_type: { type: string, const: binary }

    TriangularWindow:
      type: object
      description: |
        p25 / median / p75. Coverage quality is scored by proximity to the center
        date, so scheduling on the peak scores higher than scheduling at the edge.
        Asymmetric windows are supported.
      properties:
        left_date:    { type: [string, "null"], format: date }
        center_date:  { type: [string, "null"], format: date }
        right_date:   { type: [string, "null"], format: date }
        left_hours:   { type: [number, "null"] }
        center_hours: { type: [number, "null"] }
        right_hours:  { type: [number, "null"] }
        coverage_type: { type: string, const: graded }

    FailureBucket:
      type: object
      description: One time band of the failure distribution.
      properties:
        bucket_index: { type: integer }
        start_date:   { type: string, format: date }
        end_date:     { type: string, format: date }
        start_day:    { type: integer }
        end_day:      { type: integer }
        failure_probability:
          type: [number, "null"]
          description: MARGINAL probability of failing inside this band.
        cumulative_probability:
          type: [number, "null"]
          description: Probability of having failed by the end of this band.
        conditional_failure_probability:
          type: [number, "null"]
          description: |
            Probability of failing in this band GIVEN survival to its start. Usually
            the one an optimizer wants for a cost-of-delay term.
        survival_probability: { type: [number, "null"] }
        hazard_per_flight_hour: { type: [number, "null"] }
        severity_coefficient:
          type: [number, "null"]
          description: Marginal probability scaled by health decline, for objective weighting.
        risk_level: { type: [string, "null"] }

    ComponentFailureWindow:
      type: object
      properties:
        component_id:         { type: integer }
        component_name:       { type: [string, "null"] }
        component_type:       { type: [string, "null"] }
        aircraft_tail_number: { type: [string, "null"] }
        current_health_score: { type: [number, "null"] }
        predicted_rul_hours:  { type: [number, "null"] }
        prediction_confidence: { type: [number, "null"] }
        horizon_days:         { type: integer }
        bucket_days:          { type: integer }
        failure_probability_buckets:
          type: array
          items: { $ref: "#/components/schemas/FailureBucket" }
        peak_risk_bucket:
          type: [integer, "null"]
          description: Index of the band carrying the highest marginal probability.
        failure_window:
          type: object
          description: Present unless include_window=false.
          properties:
            rectangular: { $ref: "#/components/schemas/RectangularWindow" }
            triangular:  { $ref: "#/components/schemas/TriangularWindow" }
            rul_distribution: { $ref: "#/components/schemas/RulDistribution" }
            confidence_adjusted:
              type: boolean
              description: Whether prediction confidence widened or tightened the spread.
        distribution:
          type: object
          additionalProperties: true
          description: The fitted model the buckets were drawn from, so the numbers are auditable.
        utilization_hours_per_day: { type: [number, "null"] }
        utilization_source:
          type: [string, "null"]
          description: "Where the utilization rate came from: an override, the aircraft's own history, or a default."

    FailureWindowsResponse:
      type: object
      properties:
        success:      { type: boolean }
        components:
          type: array
          items: { $ref: "#/components/schemas/ComponentFailureWindow" }
        total_count:  { type: integer }
        horizon_days: { type: integer }
        bucket_days:  { type: integer }
        num_buckets:  { type: integer }
        generated_at: { type: string, format: date-time }

    FailureEvent:
      type: object
      description: "One sampled failure: this component, on this aircraft, on this day."
      properties:
        component_id:   { type: integer }
        component_name: { type: [string, "null"] }
        component_type: { type: [string, "null"] }
        part_number:    { type: [string, "null"] }
        aircraft_id:    { type: [integer, "null"] }
        tail_number:    { type: [string, "null"] }
        aircraft_type:  { type: [string, "null"] }
        failure_day:    { type: integer, description: Days from today. }
        failure_date:   { type: string, format: date }
        failure_hours:  { type: number }
        natural_probability:
          type: number
          description: This component's own probability of failing inside the horizon, before calibration.
        current_health_score: { type: [number, "null"] }

    FailureInstance:
      type: object
      description: One independent realization of the scenario.
      properties:
        instance:    { type: integer }
        seed:        { type: integer }
        event_count: { type: integer }
        events:
          type: array
          items: { $ref: "#/components/schemas/FailureEvent" }

    FailureInstancesResponse:
      type: object
      properties:
        success:          { type: boolean }
        company_id:       { type: integer }
        total_components: { type: integer }
        horizon_days:     { type: integer }
        unscheduled_pct:  { type: number }
        utilization_hours_per_day: { type: [number, "null"] }
        base_seed:        { type: integer }
        calibration:
          type: object
          description: How the requested rate was turned into events. Published so the sampling is auditable.
          properties:
            target_events:
              type: number
              description: Events implied by the requested rate.
            natural_expected_events:
              type: number
              description: Events the models expect with no calibration applied.
            intensity_multiplier:
              type: number
              description: |
                The shared multiplier applied to every component's probability to hit
                the target. Relative risk between components is unchanged.
            note: { type: string }
        instances:
          type: array
          items: { $ref: "#/components/schemas/FailureInstance" }
        generated_at:     { type: string, format: date-time }
        reproducibility:  { type: string }

    OptimizerSlot:
      type: object
      description: One scheduled item from your optimizer.
      properties:
        component_id:         { type: integer }
        scheduled_date:       { type: string, format: date }
        aircraft_tail_number: { type: string }
        task_type:            { type: string }
        bundled_with:
          type: array
          items: { type: integer }
          description: Other component ids bundled into the same shop visit.
      required: [component_id, scheduled_date]

    OptimizerFeedback:
      type: object
      properties:
        slots:
          type: array
          items: { $ref: "#/components/schemas/OptimizerSlot" }
        optimizer_result_id:   { type: string }
        optimizer_scenario_id: { type: string }
        optimizer_generation:  { type: integer }
        result_availability:
          type: number
          description: Fleet availability the plan achieved, as a percent.
        result_fh_per_month:   { type: number }
        sla_compliant:         { type: boolean }
      required: [slots]

    Error:
      type: object
      properties:
        success:
          type: boolean
          const: false
        error:
          type: string
          description: A human-readable description of what went wrong.
      required: [error]

    ConfigResponse:
      type: object
      properties:
        success: { type: boolean }
        access_level:
          type: string
          enum: [read_only, read_write, full]
        allowed_endpoints:
          type: array
          items: { type: string }
        max_predictions:
          type: integer
          description: Maximum records a single request may return for this token.
        can_export:
          type: boolean

    FleetSummaryResponse:
      type: object
      properties:
        success: { type: boolean }
        fleet_health:
          type: number
          description: Average fleet health, 0 to 100.
        components:
          type: object
          properties:
            total: { type: integer }
            healthy: { type: integer }
            warning: { type: integer }
            critical: { type: integer }
        avg_confidence:
          type: number
          description: >-
            Composite average confidence, 0 to 1. Per-component confidence
            weighted by prediction coverage and recency, then blended with fleet
            failure risk, so it tracks fleet health rather than sitting flat.
        raw_confidence:
          type: number
          description: Plain mean of per-component confidence, 0 to 1.
        confidence_coverage:
          type: number
          description: Share of components with a usable recent prediction, 0 to 1.
        survival_30d:
          type: number
          description: Modeled share of the fleet expected to last 30 days, 0 to 1.
        anomalies: { type: integer }
        high_risk: { type: integer }
        generated_at:
          type: string
          format: date-time

    Predictions:
      type: object
      description: The model output for a component.
      properties:
        current_health_score:
          type: [number, "null"]
          description: 0 to 100, where higher is healthier.
        health_trend:
          type: [string, "null"]
          description: For example stable, declining, or critical.
        predicted_rul_hours:
          type: [number, "null"]
          description: Predicted remaining useful life, in operating hours.
        predicted_rul_days:
          type: [number, "null"]
        failure_probability:
          type: [number, "null"]
          description: 0 to 1, the modeled probability of failure in the near term.
        prediction_confidence:
          type: [number, "null"]
          description: Present once a trained model has scored this component.
        condition_indicator:
          type: [number, "null"]
        anomaly_score:
          type: [number, "null"]

    SurvivalAnalysis:
      type: object
      properties:
        hazard_rate: { type: [number, "null"] }
        survival_function:
          type: [array, "null"]
          items: { type: number }
        rul_distribution:
          type: [object, "null"]
          properties:
            min: { type: [number, "null"] }
            typical: { type: [number, "null"] }
            max: { type: [number, "null"] }

    Maintenance:
      type: object
      properties:
        optimal_action:
          type: [string, "null"]
          description: One of do_nothing, inspect, repair, or replace.
        priority:
          type: [string, "null"]
          enum: [critical, high, medium, low, null]
        action_deadline: { type: [string, "null"], format: date-time }
        action_confidence: { type: [number, "null"] }
        maintenance_due_date: { type: [string, "null"], format: date-time }

    Component:
      type: object
      properties:
        id: { type: integer }
        name: { type: [string, "null"] }
        component_type:
          type: [string, "null"]
          description: The ATA chapter the item maps to, for example "ATA 29 Hydraulic Power".
        part_number: { type: [string, "null"] }
        serial_number: { type: [string, "null"] }
        external_id: { type: [string, "null"] }
        task_reference:
          type: [string, "null"]
          description: The task identifier from the source system, so a row ties back to its task.
        aircraft_id: { type: [integer, "null"] }
        aircraft_tail_number: { type: [string, "null"] }
        manufacturer: { type: [string, "null"] }
        installation_date: { type: [string, "null"], format: date-time }
        last_maintenance_date: { type: [string, "null"], format: date-time }
        unit_cost: { type: [number, "null"] }
        lead_time_days: { type: [integer, "null"] }
        currency: { type: [string, "null"] }
        is_active: { type: boolean }
        data_source: { type: [string, "null"] }
        predictions: { $ref: "#/components/schemas/Predictions" }
        survival_analysis: { $ref: "#/components/schemas/SurvivalAnalysis" }
        maintenance: { $ref: "#/components/schemas/Maintenance" }
        anomaly_detection:
          type: object
          properties:
            anomaly_detected: { type: [boolean, "null"] }
            anomaly_score: { type: [number, "null"] }

    ComponentsSummary:
      type: object
      properties:
        total: { type: integer }
        by_priority:
          type: object
          properties:
            critical: { type: integer }
            high: { type: integer }
            medium: { type: integer }
            low: { type: integer }

    ComponentsResponse:
      type: object
      properties:
        success: { type: boolean }
        total: { type: integer }
        components:
          type: array
          items: { $ref: "#/components/schemas/Component" }
        summary: { $ref: "#/components/schemas/ComponentsSummary" }

    ForecastItem:
      type: object
      properties:
        kind:
          type: string
          description: The source of the item, for example a prediction or a work order.
        urgency:
          type: string
          enum: [critical, high, medium, normal]
        tail_number: { type: [string, "null"] }
        aircraft_type: { type: [string, "null"] }
        component_name: { type: [string, "null"] }
        component_type: { type: [string, "null"] }
        part_number: { type: [string, "null"] }
        task_reference: { type: [string, "null"] }
        failure_probability: { type: [number, "null"] }
        predicted_rul_hours: { type: [number, "null"] }
        days_until_due:
          type: [integer, "null"]
          description: Negative when the item is already past due.
        suggested_action_date: { type: [string, "null"], format: date }
        suggested_duration_hours: { type: [number, "null"] }
        rul_distribution:
          description: |
            The uncertainty around days_until_due. Additive: a client that does not
            read it is unaffected.
          oneOf:
            - $ref: "#/components/schemas/RulDistribution"
            - type: "null"

    ForecastResponse:
      type: object
      properties:
        success: { type: boolean }
        total: { type: integer }
        items:
          type: array
          items: { $ref: "#/components/schemas/ForecastItem" }
        window:
          type: object
          properties:
            start: { type: string, format: date }
            end: { type: string, format: date }
            days: { type: integer }
        counts:
          type: object
          properties:
            critical: { type: integer }
            high: { type: integer }
            medium: { type: integer }
            normal: { type: integer }
            predictions: { type: integer }
            work_orders: { type: integer }
        filters_applied:
          type: object
          additionalProperties: true

    Aircraft:
      type: object
      properties:
        id: { type: integer }
        tail_number: { type: [string, "null"] }
        aircraft_type: { type: [string, "null"] }
        manufacturer: { type: [string, "null"] }
        model: { type: [string, "null"] }
        total_flight_hours: { type: [number, "null"] }
        total_flight_cycles: { type: [integer, "null"] }
        is_active: { type: boolean }

    Scenario:
      type: object
      properties:
        id: { type: integer }
        name: { type: [string, "null"] }
        description: { type: [string, "null"] }
        created_at: { type: [string, "null"], format: date-time }

    ScenarioCreate:
      type: object
      properties:
        name: { type: string }
        description: { type: string }
      required: [name]

    HealthCheckResponse:
      type: object
      description: Service status plus a readback of the calling token's entitlements.
      properties:
        status:
          type: string
          enum: [healthy, degraded]
          description: "`degraded` means the service answered but a dependency did not."
        timestamp:   { type: string, format: date-time }
        version:     { type: string, description: Version of the running service. }
        api_contract: { type: string, description: "The contract these paths implement, for example `v1`." }
        build:       { type: string, description: Deployed build identifier, when the platform supplies one. }
        authenticated: { type: boolean }
        auth_method: { type: string, enum: [api_token, session] }
        database:    { type: string, enum: [connected, unavailable] }
        company:     { type: [string, "null"] }
        company_id:  { type: [integer, "null"] }
        access_level: { type: [string, "null"], enum: [read_only, read_write, full, null] }
        max_predictions_per_request: { type: [integer, "null"] }
        expires_at:  { type: [string, "null"], format: date-time }
        rate_limit:  { $ref: "#/components/schemas/RateLimitStatus" }

    RateLimitStatus:
      type: object
      description: |
        Quota remaining for the calling token. The top-level `remaining`,
        `total`, and `reset_time` describe the monthly quota; the `daily` and
        `monthly` blocks give both explicitly.
      properties:
        remaining:  { type: integer }
        total:      { type: integer }
        reset_time: { type: string, format: date-time }
        daily:      { $ref: "#/components/schemas/QuotaWindow" }
        monthly:    { $ref: "#/components/schemas/QuotaWindow" }

    QuotaWindow:
      type: object
      properties:
        used:       { type: integer }
        total:      { type: integer }
        remaining:  { type: integer }
        reset_time: { type: string, format: date-time }

    TokenInfo:
      type: object
      description: Identity, scope, and usage of the calling token.
      properties:
        name:          { type: string }
        email:         { type: string, format: email }
        organization:  { type: [string, "null"] }
        access_level:  { type: string, enum: [read_only, read_write, full] }
        company_id:    { type: [integer, "null"] }
        requires_login: { type: boolean }
        expires_at:    { type: string, format: date-time }
        days_remaining: { type: integer }
        daily_limit:     { type: integer }
        daily_usage:     { type: integer }
        daily_remaining: { type: integer }
        monthly_limit:     { type: integer }
        monthly_usage:     { type: integer }
        monthly_remaining: { type: integer }
        max_predictions_per_request: { type: integer }

    SensorReading:
      type: object
      description: |
        One telemetry sample for one component. Every sensor value is optional;
        supply the ones the source system produces. Values outside their valid
        range cause that row to be rejected while the rest of the batch lands.
      required: [component_id, timestamp]
      properties:
        component_id: { type: integer }
        timestamp:
          type: string
          format: date-time
          description: ISO 8601. A trailing `Z` is accepted.
        external_id:
          type: [string, "null"]
          maxLength: 100
          description: Your identifier for this reading. Makes retries idempotent.
        external_system:
          type: [string, "null"]
          maxLength: 50
          description: Source system that produced the reading.
        temperature:     { type: [number, "null"], minimum: -50,  maximum: 300,   description: Degrees Celsius. }
        vibration:       { type: [number, "null"], minimum: 0,    maximum: 50,    description: mm/s RMS. }
        pressure:        { type: [number, "null"], minimum: 0,    maximum: 1000 }
        rpm:             { type: [number, "null"], minimum: 0,    maximum: 50000 }
        oil_temperature: { type: [number, "null"], minimum: -50,  maximum: 300,   description: Degrees Celsius. }
        fuel_flow:       { type: [number, "null"], minimum: 0,    maximum: 1000,  description: Gallons per hour. }
        voltage:         { type: [number, "null"], minimum: 0,    maximum: 100,   description: Volts DC. }
        current:         { type: [number, "null"], minimum: 0,    maximum: 100,   description: Amps. }
        raw_data:
          type: [object, "null"]
          additionalProperties: true
          description: Any additional fields, stored alongside the reading.

    BatchIngestResponse:
      type: object
      description: |
        The outcome of a batch. A partial success is normal and is reported
        rather than raised: rows that failed are listed in `errors` with the
        index they occupied in the request, and the rest are stored.
      properties:
        success:    { type: boolean }
        received:   { type: integer, description: Rows in the request. }
        stored:     { type: integer, description: Rows written. }
        duplicates: { type: integer, description: Rows already present, deduped rather than stored. }
        rejected:   { type: integer, description: Rows that failed validation or tenant scoping. }
        errors:
          type: array
          description: Up to 50 rejections. Check `errors_truncated` for more.
          items: { $ref: "#/components/schemas/BatchIngestError" }
        errors_truncated: { type: boolean }

    BatchIngestError:
      type: object
      properties:
        row:
          type: integer
          description: Zero-based index of the row in the submitted array.
        reason:
          type: string
          description: |
            Machine-readable cause, for example `component_not_accessible`,
            `invalid_timestamp`, `missing_or_invalid_component_id`, or
            `value_invalid:<sensor>:<why>`.
        component_id: { type: [integer, "null"] }
        sensor:       { type: [string, "null"] }
        value:        { description: The offending value, when the failure was a value. }