openapi: 3.1.0
info:
  contact: {}
  description: |-
    OpenAPI surface for the GA Flight backend (EASA Part-FCL flight-school operations platform).

    Authentication
    -------------
    Most routes require a Bearer JWT issued by /auth/login. Tenant
    scoping is path-based (/orgs/{orgId}/...). Public verification
    and ICS-subscription routes are token-gated by a sha256-hashed
    share token (?token=...).

    Outbound webhooks
    -----------------
    Configure HTTPS endpoints from the integrations admin to receive
    signed event deliveries. Supported events: flight.submitted,
    flight.approved, flight.rejected, maintenance.alert,
    compliance.updated, training.signoff.recorded, payment.received,
    vtr.issued, medical.expiring. Each delivery is HMAC-SHA256 signed
    over the raw body using the endpoint secret; verify via the
    X-GA-Webhook-Signature header. See docs/platform/events.md.

    Stability
    ---------
    The spec is generated by swag (Swagger 2.0). Routes marked
    "beta" in the description may evolve; the catalog at
    /orgs/{orgId}/integrations/event-catalog is the source of truth
    for stability per event type.
  title: GA Flight Backend API
  version: '1.0'
paths:
  /admin/addon-listings:
    get:
      description: |-
        Returns listings filtered by review_status. Defaults to
        pending_review when the query param is omitted. Gated by
        platform_admin.listing:review.
      parameters:
        - description: 'Review status filter (default: pending_review)'
          in: query
          name: review_status
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List addon listings in the review queue
      tags:
        - platform-addon-listings-admin
  /admin/addon-listings/{listingId}:
    get:
      description: |-
        Returns full listing detail across orgs (no owner_org filter).
        Gated by platform_admin.listing:review.
      parameters:
        - description: Addon listing ID
          in: path
          name: listingId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get an addon listing (admin)
      tags:
        - platform-addon-listings-admin
  /admin/addon-listings/{listingId}/approve:
    post:
      description: |-
        Transitions pending_review -> approved AND sets status=public.
        Body : {review_notes?: string}. Notes are optional on approve.
        Returns 403 on conflict-of-interest, 409 if the listing is not
        currently in pending_review. Gated by
        platform_admin.listing:review.
      parameters:
        - description: Addon listing ID
          in: path
          name: listingId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Approve an addon listing
      tags:
        - platform-addon-listings-admin
  /admin/addon-listings/{listingId}/reject:
    post:
      description: |-
        Transitions pending_review -> rejected AND drops status to
        private. Body : {review_notes: string} REQUIRED, >= 40 chars
        after trim (spec §3.3 — rejection feedback must be substantive).
        Returns 403 on conflict-of-interest, 409 if the listing is not
        currently in pending_review, 400 if notes are too short.
        Gated by platform_admin.listing:review.
      parameters:
        - description: Addon listing ID
          in: path
          name: listingId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Reject an addon listing
      tags:
        - platform-addon-listings-admin
  /admin/directory/{type}/{id}:
    patch:
      description: Allows an authenticated admin to update moderation fields (status, featured, sort_priority) on any directory listing.
      parameters:
        - description: Listing type (schools, instructors, or aircraft)
          in: path
          name: type
          required: true
          schema:
            type: string
        - description: Listing ID
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.directoryAdminPatchRequest'
        description: Fields to update
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Admin-moderate a directory listing
      tags:
        - directory
  /api/v1/public/compliance:
    get:
      description: Returns aggregated compliance record counts for the organization scoped by the API key.
      parameters:
        - description: Days threshold for expiring-soon filter
          in: query
          name: soon_days
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get compliance summary (public API)
      tags:
        - public-api
  /api/v1/public/events:
    get:
      description: Returns integration events for the organization scoped by the API key, with optional type and date filters.
      parameters:
        - description: Event type filter
          in: query
          name: type
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: since
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: until
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List integration events (public API)
      tags:
        - public-api
  /api/v1/public/events/{eventId}/replay:
    post:
      description: Re-dispatches an integration event by ID for the organization scoped by the API key.
      parameters:
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Replay an integration event (public API)
      tags:
        - public-api
  /api/v1/public/finance-summaries:
    get:
      description: Returns monthly financial snapshots for the organization scoped by the API key.
      parameters:
        - description: Max months to return
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get finance summaries (public API)
      tags:
        - public-api
  /api/v1/public/fleet:
    get:
      description: Returns fleet aircraft with maintenance and work order counts for the organization scoped by the API key.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get fleet summary (public API)
      tags:
        - public-api
  /api/v1/public/flights:
    get:
      description: Returns a paginated list of flights for the organization scoped by the API key, with optional date and status filters.
      parameters:
        - description: Flight status filter
          in: query
          name: status
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List flights (public API)
      tags:
        - public-api
  /api/v1/public/invoices:
    get:
      description: Returns a paginated list of student invoices for the organization scoped by the API key.
      parameters:
        - description: 'Filter: draft|sent|paid|overdue|void'
          in: query
          name: status
          schema:
            type: string
        - description: ISO timestamp lower bound on issued_on
          in: query
          name: from
          schema:
            type: string
        - description: ISO timestamp upper bound on issued_on
          in: query
          name: to
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List invoices (public API)
      tags:
        - public-api
  /api/v1/public/invoices/{invoiceId}:
    get:
      description: Returns a single student invoice (with line items) for the organization scoped by the API key.
      parameters:
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get invoice detail (public API)
      tags:
        - public-api
  /api/v1/public/maintenance/due:
    get:
      description: Returns per-aircraft maintenance due/overdue item counts for the organization.
      parameters:
        - description: Filter to a specific aircraft
          in: query
          name: aircraft_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Aircraft maintenance due summary (public API)
      tags:
        - public-api
  /api/v1/public/maintenance/work-orders:
    get:
      description: Returns a paginated list of work orders for the organization scoped by the API key.
      parameters:
        - description: Filter by aircraft
          in: query
          name: aircraft_id
          schema:
            type: string
        - description: 'Filter: open|in_progress|done'
          in: query
          name: status
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List maintenance work orders (public API)
      tags:
        - public-api
  /api/v1/public/maintenance/work-orders/{workOrderId}:
    get:
      description: Returns a single work order for the organization scoped by the API key.
      parameters:
        - description: Work order ID
          in: path
          name: workOrderId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get maintenance work order detail (public API)
      tags:
        - public-api
  /api/v1/public/pilots:
    get:
      description: Returns a paginated list of pilots for the organization scoped by the API key.
      parameters:
        - description: Person type filter (pilot, instructor, all)
          in: query
          name: type
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List pilots (public API)
      tags:
        - public-api
  /api/v1/public/planning/sessions:
    get:
      description: Returns paginated planning events for the organization scoped by the API key.
      parameters:
        - description: 'Filter by kind: flight|lesson|maintenance|ground|other'
          in: query
          name: kind
          schema:
            type: string
        - description: ISO timestamp lower bound on start_at
          in: query
          name: from
          schema:
            type: string
        - description: ISO timestamp upper bound on start_at
          in: query
          name: to
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List planning events (public API)
      tags:
        - public-api
  /api/v1/public/planning/sessions/{sessionId}:
    get:
      description: Returns a single planning event for the organization scoped by the API key.
      parameters:
        - description: Planning event ID
          in: path
          name: sessionId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get planning event detail (public API)
      tags:
        - public-api
  /api/v1/public/training-progress:
    get:
      description: Returns per-student training progress and compliance status for the organization scoped by the API key.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get training progress (public API)
      tags:
        - public-api
  /api/v1/public/training/signoffs:
    get:
      description: Returns paginated training signoffs for the organization scoped by the API key.
      parameters:
        - description: Filter by student
          in: query
          name: student_id
          schema:
            type: string
        - description: ISO timestamp lower bound on signed_at
          in: query
          name: from
          schema:
            type: string
        - description: ISO timestamp upper bound on signed_at
          in: query
          name: to
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List training signoffs (public API)
      tags:
        - public-api
  /api/v1/public/vtr:
    get:
      description: Returns a paginated list of evidence packs (VTRs) issued by the organization.
      parameters:
        - description: Filter by pack type (e.g. pilot_portable, compliance_pack)
          in: query
          name: type
          schema:
            type: string
        - description: Filter by status (generating|ready|failed)
          in: query
          name: status
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: List VTRs / evidence packs (public API)
      tags:
        - public-api
  /api/v1/public/vtr/{vtrId}:
    get:
      description: Returns a single evidence pack's metadata (no raw certificate payload).
      parameters:
        - description: Evidence pack ID
          in: path
          name: vtrId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - ApiKeyAuth: []
      summary: Get VTR / evidence pack detail (public API)
      tags:
        - public-api
  /applications:
    get:
      description: Returns a paginated list of all opportunity applications visible to the authenticated user's organization.
      parameters:
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List all applications
      tags:
        - marketplace
  /applications/{applicationId}:
    get:
      description: Returns a single opportunity application by its ID.
      parameters:
        - description: Application ID
          in: path
          name: applicationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get application by ID
      tags:
        - marketplace
  /applications/{applicationId}/status:
    patch:
      description: Updates the status of an opportunity application (e.g., shortlisted, rejected, accepted).
      parameters:
        - description: Application ID
          in: path
          name: applicationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update application status
      tags:
        - marketplace
  /auth/establish-exchange:
    post:
      description: Public endpoint. Validates the OTT, marks it consumed, and issues an
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.establishExchangeRequest'
        description: One-time token
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: SERVER_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Exchange a one-time handoff token for a full session
      tags:
        - auth
  /auth/establish-token:
    post:
      description: Authenticated endpoint. Generates a short-lived (60s), single-use token
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.establishTokenRequest'
        description: Optional account_type (org|pilot)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: SERVER_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Issue a one-time session-handoff token
      tags:
        - auth
  /auth/login:
    post:
      description: Authenticates a user with email and password. Returns a token pair on success, or an MFA challenge if two-factor is enabled.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.loginRequest'
        description: Login payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiAuthTokenPairResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Login
      tags:
        - auth
  /auth/logout:
    post:
      description: Revokes the session identified by the provided refresh token (body or cookie) and clears auth cookies.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.refreshRequest'
        description: Optional refresh token
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties:
                  type: string
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Logout and invalidate the current session
      tags:
        - auth
  /auth/mfa/enroll:
    delete:
      description: Deletes the enrollment row and burns all recovery codes.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Disable an active MFA enrollment
      tags:
        - auth
  /auth/mfa/enroll/start:
    post:
      description: Generates a fresh TOTP secret and persists it with
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.mfaEnrollStartRequest'
        description: Optional issuer/account labels
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Begin MFA TOTP enrollment
      tags:
        - auth
  /auth/mfa/enroll/verify:
    post:
      description: Submits the first valid TOTP code from the authenticator
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.mfaEnrollVerifyRequest'
        description: Verify payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Confirm MFA TOTP enrollment with a code
      tags:
        - auth
  /auth/mfa/recovery-codes:
    get:
      description: Returns counts only — never the codes themselves. The UI
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Report how many recovery codes the caller still has
      tags:
        - auth
  /auth/mfa/recovery-codes/regenerate:
    post:
      description: Burns every existing code (used or unused) and returns a
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '412':
          description: PRECONDITION_FAILED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Regenerate the caller's MFA recovery codes
      tags:
        - auth
  /auth/mfa/step:
    post:
      description: Verifies a fresh TOTP or recovery code from the authenticated
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.mfaStepRequest'
        description: Step-up payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED — invalid code
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '412':
          description: PRECONDITION_FAILED — mfa not enrolled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Step-up MFA verification for sensitive operations
      tags:
        - auth
  /auth/mfa/verify:
    post:
      description: Completes a two-factor authentication challenge using the MFA token returned by login and a TOTP code. Issues a full session token pair on success.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.mfaVerifyRequest'
        description: MFA verify payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiAuthTokenPairResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Verify MFA challenge and issue full session
      tags:
        - auth
  /auth/oauth/callback:
    get:
      parameters:
        - description: Authorization code
          in: query
          name: code
          required: true
          schema:
            type: string
        - description: State token
          in: query
          name: state
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
        '401':
          description: Unauthorized
        '502':
          description: Bad Gateway
      summary: Consumer OAuth callback
      tags:
        - auth
  /auth/oauth/start:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.oauthStartRequest'
        description: OAuth start payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '502':
          description: Bad Gateway
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Start consumer OAuth flow
      tags:
        - auth
  /auth/org-invites/{token}:
    get:
      description: Returns the public state of an organization invite by token. Used to display invite details before claiming.
      parameters:
        - description: Invite token
          in: path
          name: token
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT - already used
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '410':
          description: GONE - expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get organization invite details
      tags:
        - auth
  /auth/org-invites/claim:
    post:
      description: Claims an organization invite, creating a user account if necessary and adding the user as a member. Returns session tokens for new accounts.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.claimOrgInviteRequest'
        description: Claim details
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT - already used or account exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '410':
          description: GONE - expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Claim an organization invite
      tags:
        - auth
  /auth/refresh:
    post:
      description: Exchanges a valid refresh token for a new access/refresh token pair. The previous refresh token is rotated.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.refreshRequest'
        description: Refresh payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiAuthTokenPairResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Refresh access token
      tags:
        - auth
  /auth/sessions:
    delete:
      description: Revokes every active session for the calling user, effectively logging them out of all devices.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties:
                  type: string
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete all sessions for the authenticated user
      tags:
        - auth
  /auth/signup:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.signupRequest'
        description: Signup payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Signup (standalone user)
      tags:
        - auth
  /auth/signup-org:
    post:
      description: Creates a new user account and organization in one step. The email becomes the user login credential and the caller is granted the owner role.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.signupOrgRequest'
        description: Signup + organization creation payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Signup and create organization
      tags:
        - auth
  /auth/student-invites/{token}:
    get:
      description: Public endpoint. Validates a student invite token and returns invite details. Returns 410 if expired, 409 if already claimed.
      parameters:
        - description: Invite token
          in: path
          name: token
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT — invite no longer valid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '410':
          description: GONE — invite expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get student invite
      tags:
        - students
  /auth/student-invites/claim:
    post:
      description: Public endpoint. Claims a student invite by token. Creates a new user account if not authenticated, or links the existing session user. Returns auth tokens when a new account is created.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.claimStudentInviteRequest'
        description: Claim payload (token required; password required if not authenticated)
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT — already claimed or email mismatch
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '410':
          description: GONE — invite expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Claim student invite
      tags:
        - students
  /capabilities:
    get:
      description: Returns module availability using schema presence and feature flags.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.capabilitiesResponse'
      summary: Get backend capabilities
      tags:
        - capabilities
  /debug/schedulers:
    get:
      description: Returns recent runs for all schedulers grouped by name. Only available when debug endpoints are enabled.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: List all scheduler debug info
      tags:
        - internal
  /debug/schedulers/{name}:
    get:
      description: Returns recent runs for a specific scheduler. Only available when debug endpoints are enabled.
      parameters:
        - description: Scheduler name
          in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get scheduler debug info by name
      tags:
        - internal
  /docs:
    get:
      responses:
        '200':
          description: HTML
          content:
            text/html:
              schema:
                type: string
      summary: Interactive API documentation (Stoplight Elements)
      tags:
        - openapi
  /health:
    get:
      responses:
        '200':
          description: OK
          content:
            text/plain:
              schema:
                type: string
      summary: Health check
      tags:
        - health
  /internal/compliance-evaluations:
    get:
      description: Returns paginated compliance evaluations for the organization. Only available in dev mode.
      parameters:
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List internal compliance evaluations
      tags:
        - internal
  /internal/risk-signals:
    get:
      description: Returns paginated risk signals for the organization. Only available in dev mode.
      parameters:
        - description: Filter by signal date (YYYY-MM-DD)
          in: query
          name: as_of_date
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List internal risk signals
      tags:
        - internal
  /internal/structural-status:
    get:
      description: Returns internal structural health indicators including domain events, daily metrics, risk signals, and compliance evaluations.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get structural status
      tags:
        - internal
  /live:
    get:
      description: Returns 200 with no I/O — confirms the process is running.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties:
                  type: string
                type: object
      summary: Liveness probe
      tags:
        - health
  /me:
    get:
      description: Returns authenticated user profile and memberships; clients can switch org context using memberships[].organization_id.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiMeResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get current user
      tags:
        - auth
  /me/applications:
    get:
      description: Returns all opportunity applications submitted by the authenticated user.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List my applications
      tags:
        - marketplace
  /me/applications/{applicationId}/withdraw:
    post:
      description: Withdraws an opportunity application submitted by the authenticated user.
      parameters:
        - description: Application ID
          in: path
          name: applicationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Withdraw an application
      tags:
        - marketplace
  /me/balance:
    get:
      description: Returns the financial balance summary (planned, paid, outstanding) for the authenticated user (with state-discriminated envelope). Unlinked/Solo users receive an empty balance payload.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my financial balance
      tags:
        - student-portal
  /me/compliance/digest-preference:
    get:
      description: |-
        Returns the caller's compliance digest preference. If no
        row exists yet, returns the platform defaults (weekly,
        Monday, EN).
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.complianceDigestPrefDTO'
      security:
        - BearerAuth: []
      summary: Get my compliance digest preference
      tags:
        - compliance
    patch:
      description: |-
        Patches the caller's compliance digest preference. Any
        field omitted from the body is left unchanged. Validates
        cadence in (weekly, off), weekday in [1, 7], locale in
        (en, fr).
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.patchComplianceDigestPrefRequest'
        description: Patch body
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.complianceDigestPrefDTO'
      security:
        - BearerAuth: []
      summary: Update my compliance digest preference
      tags:
        - compliance
  /me/day-of-flight/acknowledge-blocker:
    post:
      description: Records a pilot_blocker_acknowledgments row. This does NOT resolve the underlying compliance issue — it only records that the human became aware. Org admins can audit.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.acknowledgeBlockerPayload'
        description: Acknowledge payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.pilotBlockerAckResponse'
      security:
        - BearerAuth: []
      summary: Mark a blocker as seen by the pilot
      tags:
        - pilot-day
  /me/day-of-flight/snapshot:
    get:
      description: Returns a single bundle with today's flights, next flight, compliance blockers, inbox preview, and per-section availability hints. Each call is computed live; the etag header lets a future native client short-circuit identical responses without re-rendering.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.pilotDayBundle'
        '304':
          description: Not Modified
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get the pilot day-of-flight snapshot
      tags:
        - pilot-day
  /me/enrollments:
    get:
      description: Returns training program enrollments for the authenticated user (with state-discriminated envelope). Unlinked/Solo users receive an empty enrollment list; soft-deleted student pilots retain access to archived enrollments.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
        - description: Filter by enrollment status
          in: query
          name: status
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my enrollments
      tags:
        - student-portal
  /me/invoices:
    get:
      description: Returns a paginated list of training invoices for the authenticated user (with state-discriminated envelope). Unlinked/Solo users receive an empty invoice list; soft-deleted student pilots retain access to archived invoices.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
        - description: Filter by invoice status
          in: query
          name: status
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page (max 100)
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my invoices
      tags:
        - student-portal
  /me/logbook:
    get:
      description: Returns a paginated list of training flight entries for the authenticated user (with state-discriminated envelope). Unlinked/Solo users receive an empty logbook list; soft-deleted student pilots retain access to archived flights.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page (max 200)
          in: query
          name: page_size
          schema:
            type: integer
        - description: Filter by flight type
          in: query
          name: flight_type
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my logbook entries
      tags:
        - student-portal
  /me/logbook.csv:
    get:
      description: Exports training flight entries for the authenticated user as a downloadable CSV file (with state-discriminated envelope via the X-Me-State response header). Unlinked/Solo users receive a header-only CSV; soft-deleted student pilots retain access to archived flights.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
        - description: Filter by flight type
          in: query
          name: flight_type
          schema:
            type: string
      responses:
        '200':
          description: CSV file
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: unauthenticated
          content:
            text/csv:
              schema:
                type: string
      security:
        - BearerAuth: []
      summary: Export my logbook as CSV
      tags:
        - student-portal
  /me/logbook/milestones:
    get:
      description: Returns deduplicated training milestones achieved by the authenticated user (with state-discriminated envelope), sorted by most recent first. Unlinked/Solo users receive an empty milestone list; soft-deleted student pilots retain access to archived milestones.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my training milestones
      tags:
        - student-portal
  /me/logbook/totals:
    get:
      description: Returns aggregated flight hour totals and EASA PPL(A) progress for the authenticated user (with state-discriminated envelope). Unlinked/Solo users receive zeroed totals; soft-deleted student pilots retain access to archived hours.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my logbook hour totals
      tags:
        - student-portal
  /me/payments:
    get:
      description: Returns a paginated list of payments for the authenticated user (with state-discriminated envelope). Unlinked/Solo users receive an empty payments list; soft-deleted student pilots retain access to archived payments.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
        - description: Filter by payment status
          in: query
          name: status
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page (max 100)
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my payments
      tags:
        - student-portal
  /me/payments/{paymentId}/receipt.pdf:
    get:
      description: Returns a PDF receipt for one of the authenticated user's
      parameters:
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: PDF receipt document
          content:
            application/pdf:
              schema:
                type: string
        '401':
          description: UNAUTHORIZED
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: PDF_SERVICE_UNAVAILABLE
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Download my payment receipt PDF (student)
      tags:
        - student-portal
  /me/payments/checkout:
    post:
      description: Creates a Stripe checkout session for a student to pay an outstanding invoice. Returns a checkout URL. Unlinked/Solo users receive 403 `payments_require_student_linkage` — paying an invoice requires being a student at the invoicing organization.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.mePaymentCheckoutRequest'
        description: Checkout details
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN - payments_require_student_linkage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT - already paid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE - invoice not payable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a payment checkout session
      tags:
        - student-portal
  /me/progress:
    get:
      description: Returns an overall training progress summary for the authenticated user (with state-discriminated envelope) including completion percentage, EASA PPL(A) breakdown, and next pending milestones. Unlinked/Solo users receive a zeroed progress payload; soft-deleted student pilots retain access to archived progress.
      parameters:
        - description: Filter by organization ID
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my training progress
      tags:
        - student-portal
  /me/sessions:
    get:
      description: Returns the caller's own active sessions with best-effort device
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List active sessions for the authenticated user
      tags:
        - sessions
  /me/sessions/{sessionId}:
    delete:
      description: Deletes the session row identified by sessionId, but only
      parameters:
        - description: Session ID
          in: path
          name: sessionId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke one of the caller's own sessions
      tags:
        - sessions
  /me/sessions/others:
    post:
      description: Useful when a user notices an unfamiliar device in the
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke every session for the caller except the current one
      tags:
        - sessions
  /me/talent:
    get:
      description: Returns the authenticated user's talent profile, creating one if it does not exist.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my talent profile
      tags:
        - marketplace
    patch:
      description: Updates the authenticated user's talent profile fields such as headline, bio, skills, and availability.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update my talent profile
      tags:
        - marketplace
  /notifications/digest/peek:
    get:
      description: Returns the unflushed digest queue for the authenticated user.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      summary: Peek my pending digest entries
      tags:
        - notifications
  /notifications/digest/run:
    post:
      description: Manually flushes the daily/weekly digest queue. Requires automation.manage permission.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      summary: Flush pending digests for a mode (admin)
      tags:
        - notifications
  /notifications/preferences:
    get:
      description: Returns the effective preference per topic for the authenticated user.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  type: object
                type: array
      summary: List my notification preferences
      tags:
        - notifications
  /notifications/preferences/{topic}:
    put:
      parameters:
        - description: Topic code
          in: path
          name: topic
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      summary: Update my notification preference for a topic
      tags:
        - notifications
  /notifications/settings:
    get:
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      summary: Get my global notification settings (snooze)
      tags:
        - notifications
  /notifications/settings/snooze:
    put:
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      summary: Set or clear my global snooze
      tags:
        - notifications
  /notifications/topics:
    get:
      description: Returns the catalog of notification topics for the preferences UI.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  type: object
                type: array
      summary: List notification topics
      tags:
        - notifications
  /openapi.json:
    get:
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Get OpenAPI spec
      tags:
        - openapi
  /opportunities:
    get:
      description: Returns a paginated list of opportunities, optionally filtered by role, status, and organization scope.
      parameters:
        - description: Role filter
          in: query
          name: role
          schema:
            type: string
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List opportunities
      tags:
        - marketplace
    post:
      description: Creates a new opportunity listing for the organization.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an opportunity
      tags:
        - marketplace
  /opportunities/{opportunityId}:
    get:
      description: Returns a single opportunity by its ID.
      parameters:
        - description: Opportunity ID
          in: path
          name: opportunityId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get opportunity by ID
      tags:
        - marketplace
    patch:
      description: Updates fields of an existing opportunity.
      parameters:
        - description: Opportunity ID
          in: path
          name: opportunityId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update an opportunity
      tags:
        - marketplace
  /opportunities/{opportunityId}/applications:
    get:
      description: Returns all applications submitted for a specific opportunity, with optional status filtering.
      parameters:
        - description: Opportunity ID
          in: path
          name: opportunityId
          required: true
          schema:
            type: string
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List applications for an opportunity
      tags:
        - marketplace
  /opportunities/{opportunityId}/apply:
    post:
      description: Submits an application to an opportunity on behalf of the authenticated user's talent profile.
      parameters:
        - description: Opportunity ID
          in: path
          name: opportunityId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Apply to an opportunity
      tags:
        - marketplace
  /opportunities/{opportunityId}/close:
    post:
      description: Transitions an opportunity to the closed status, removing it from active listings.
      parameters:
        - description: Opportunity ID
          in: path
          name: opportunityId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Close an opportunity
      tags:
        - marketplace
  /opportunities/{opportunityId}/publish:
    post:
      description: Transitions an opportunity to the published status, making it visible to applicants.
      parameters:
        - description: Opportunity ID
          in: path
          name: opportunityId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Publish an opportunity
      tags:
        - marketplace
  /org/talent_links:
    get:
      description: Returns talent profile links for the organization, with optional status and type filters and pagination.
      parameters:
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
        - description: Link type filter
          in: query
          name: link_type
          schema:
            type: string
        - description: Page size (default 50, max 100)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset (default 0)
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List organization talent links
      tags:
        - marketplace
    post:
      description: Links a talent profile to the organization with a specified type and status.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an organization talent link
      tags:
        - marketplace
  /org/talent_links/{linkId}:
    patch:
      description: Updates the status of an existing organization talent link.
      parameters:
        - description: Link ID
          in: path
          name: linkId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update an organization talent link
      tags:
        - marketplace
  /organizations/current:
    get:
      description: Resolves organization from multi-tenant headers/query for global routes.
      parameters:
        - description: Organization ID for context resolution
          in: header
          name: X-Org-Id
          schema:
            type: string
        - description: Alias for organization context resolution
          in: header
          name: X-Organization-Id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: org_id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiOrganizationResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get current organization (org switch context)
      tags:
        - orgs
  /orgs/{orgId}:
    get:
      description: Returns organization profile/settings for the current org scope.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiOrganizationResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get organization
      tags:
        - orgs
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.organizationPatchRequest'
        description: Organization patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiOrganizationResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update organization
      tags:
        - orgs
  /orgs/{orgId}/aircraft:
    get:
      description: Returns all active aircraft in the organization, ordered by registration.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: SERVER_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List aircraft
      tags:
        - fleet
    post:
      description: Adds a new aircraft to the organization fleet. Registration is required and must be unique within the org.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.AircraftDTO'
        description: Aircraft payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiIDEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create aircraft
      tags:
        - fleet
  /orgs/{orgId}/aircraft/{aircraftId}:
    delete:
      description: Soft-deletes an aircraft. Blocked if the aircraft has open work orders. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT — open work orders exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete aircraft
      tags:
        - fleet
    get:
      description: Returns a single aircraft by ID, including timestamps.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get aircraft
      tags:
        - fleet
    patch:
      description: Partially updates an aircraft record. Only supplied fields are changed. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.aircraftPatchRequest'
        description: Fields to update
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update aircraft
      tags:
        - fleet
  /orgs/{orgId}/aircraft/{aircraftId}/arc:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.aircraftARCDTO'
      security:
        - BearerAuth: []
      summary: Get ARC tracking info for an aircraft
      tags:
        - maintenance
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.patchARCRequest'
        description: ARC update
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.aircraftARCDTO'
      security:
        - BearerAuth: []
      summary: Update ARC tracking info for an aircraft
      tags:
        - maintenance
  /orgs/{orgId}/aircraft/{aircraftId}/due-items:
    get:
      description: Returns maintenance due items for a specific aircraft, including inspections, AD compliance, and recurring service items.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List due items for aircraft
      tags:
        - maintenance
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.DueItemCreateDTO'
        description: Due item payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiIDEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create maintenance due item
      tags:
        - maintenance
  /orgs/{orgId}/aircraft/{aircraftId}/maintenance-programs:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.maintenanceProgramDTO'
                type: array
      security:
        - BearerAuth: []
      summary: List maintenance programs for an aircraft
      tags:
        - maintenance
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.createMaintenanceProgramRequest'
        description: Program payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.maintenanceProgramDTO'
      security:
        - BearerAuth: []
      summary: Create a maintenance program for an aircraft
      tags:
        - maintenance
  /orgs/{orgId}/aircraft/{aircraftId}/performance-pack:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.aircraftPerformancePackDTO'
      security:
        - BearerAuth: []
      summary: Read the performance pack for an aircraft
      tags:
        - fleet
    put:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.aircraftPerformancePackDTO'
        description: Performance pack payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.aircraftPerformancePackDTO'
      security:
        - BearerAuth: []
      summary: Set or update the performance pack for an aircraft
      tags:
        - fleet
  /orgs/{orgId}/aircraft/{aircraftId}/work-orders:
    get:
      description: Returns maintenance work orders for a specific aircraft, including status, cost estimates, and linked due items.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List work orders for aircraft
      tags:
        - maintenance
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.WorkOrderCreateDTO'
        description: Work order payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiIDEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create maintenance work order
      tags:
        - maintenance
  /orgs/{orgId}/aircraft/{aircraftId}/work-orders/{workOrderId}:
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
        - description: Work order ID
          in: path
          name: workOrderId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.WorkOrderPatchDTO'
        description: Work order patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update maintenance work order
      tags:
        - maintenance
  /orgs/{orgId}/aircraft/{aircraftId}/work-orders/{workOrderId}/pdf:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: path
          name: aircraftId
          required: true
          schema:
            type: string
        - description: Work Order ID
          in: path
          name: workOrderId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/pdf:
              schema:
                type: string
        '503':
          description: PDF service unavailable
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Download a maintenance work order as PDF
      tags:
        - maintenance
  /orgs/{orgId}/analytics/dashboard:
    get:
      description: Returns the latest analytics dashboard snapshot for the organization, optionally refreshing the data.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Force refresh of snapshot
          in: query
          name: refresh
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get analytics dashboard snapshot
      tags:
        - analytics
  /orgs/{orgId}/analytics/fleet:
    get:
      description: Returns per-aircraft analytics metrics for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List fleet analytics metrics
      tags:
        - analytics
  /orgs/{orgId}/analytics/instructors:
    get:
      description: Returns per-instructor analytics metrics for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List instructor analytics metrics
      tags:
        - analytics
  /orgs/{orgId}/analytics/students:
    get:
      description: Returns per-student analytics metrics for the organization, with optional risk threshold filtering.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: Minimum risk score filter
          in: query
          name: risk_min
          schema:
            type: number
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student analytics metrics
      tags:
        - analytics
  /orgs/{orgId}/approved-programs:
    get:
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.listApprovedProgramsResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List FAA Part 141 approved programs for an org
      tags:
        - faa-part-141
    post:
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.createApprovedProgramRequest'
        description: Program payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.createApprovedProgramResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: MFA step missing/stale
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: Permission denied or org is not US-jurisdiction
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: Chief instructor not linked to a user account
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a Part 141 approved program (US-jurisdiction only)
      tags:
        - faa-part-141
  /orgs/{orgId}/approved-programs/{id}/stages:
    get:
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Approved program UUID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.listProgramStagesResponse'
      security:
        - BearerAuth: []
      summary: List stages of an approved Part 141 program (ordered by stage_number ASC)
      tags:
        - faa-part-141
  /orgs/{orgId}/approved-programs/{id}/stages/{stageId}/stage-checks:
    post:
      description: Inserts a stage_check + emits an Ed25519 student signoff, all
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Approved program UUID
          in: path
          name: id
          required: true
          schema:
            type: string
        - description: Program stage UUID
          in: path
          name: stageId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.createStageCheckRequest'
        description: Stage check payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.createStageCheckResponse'
      security:
        - BearerAuth: []
      summary: Record a Part 141 stage check + emit student SP-1 signoff
      tags:
        - faa-part-141
  /orgs/{orgId}/approved-programs/{id}/tco-documents:
    get:
      description: Returns the full TCO version history (ordered uploaded_at DESC).
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Approved program UUID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.listTCODocumentsResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List TCO document version history for an approved Part 141 program
      tags:
        - faa-part-141
    post:
      description: Inserts a tco_documents row + atomically updates approved_programs.tco_document_id
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Approved program UUID
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.createTCODocumentRequest'
        description: TCO document payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.createTCODocumentResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: MFA step missing/stale
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Upload a new TCO document version for an approved Part 141 program
      tags:
        - faa-part-141
  /orgs/{orgId}/audit/logs:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter from date (RFC3339 or YYYY-MM-DD)
          in: query
          name: date_from
          schema:
            type: string
        - description: Filter to date (RFC3339 or YYYY-MM-DD)
          in: query
          name: date_to
          schema:
            type: string
        - description: Filter by action
          in: query
          name: action
          schema:
            type: string
        - description: Filter by resource type
          in: query
          name: resource_type
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List org audit log
      tags:
        - audit
  /orgs/{orgId}/authority/attestations:
    get:
      description: Returns a paginated list of attestations for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List attestations
      tags:
        - authority
    post:
      description: Creates a new signed attestation for a specific resource within the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an attestation
      tags:
        - authority
  /orgs/{orgId}/authority/attestations/{attestationId}:
    get:
      description: Returns a single attestation by its ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Attestation ID
          in: path
          name: attestationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get attestation by ID
      tags:
        - authority
  /orgs/{orgId}/authority/chain/status:
    get:
      description: Returns the current integrity status of the authority hash chain, including data residency information.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get authority chain status
      tags:
        - authority
  /orgs/{orgId}/authority/chain/verify:
    post:
      description: Verifies the integrity of the authority hash chain for an optional sequence or date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Verify authority chain integrity
      tags:
        - authority
  /orgs/{orgId}/authority/packs:
    get:
      description: Returns a paginated list of evidence packs for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List evidence packs
      tags:
        - authority
    post:
      description: Creates a new signed evidence pack for the organization covering an optional date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an evidence pack
      tags:
        - authority
  /orgs/{orgId}/authority/packs/{packId}:
    get:
      description: Returns a single evidence pack by its ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Evidence Pack ID
          in: path
          name: packId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get evidence pack by ID
      tags:
        - authority
  /orgs/{orgId}/authority/packs/{packId}/download.zip:
    get:
      description: Builds and downloads the evidence pack archive as a ZIP file. Records an audit entry for the download.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Evidence Pack ID
          in: path
          name: packId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            application/zip:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/zip:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Download evidence pack as ZIP
      tags:
        - authority
  /orgs/{orgId}/authority/packs/{packId}/manifest:
    get:
      description: Returns the signed manifest of an evidence pack, listing all included items and their hashes.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Evidence Pack ID
          in: path
          name: packId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get evidence pack manifest
      tags:
        - authority
  /orgs/{orgId}/authority/packs/{packId}/revoke:
    post:
      description: |-
        Marks an evidence pack as revoked. The pack row, its
        manifest, signature, and items are NOT deleted — revocation
        is a metadata flag surfaced by the public verify page as
        verification_status="revoked". Existing share tokens still
        resolve, so the examiner/recruiter sees the revocation
        explicitly rather than a broken link.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Evidence pack ID
          in: path
          name: packId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.packRevokeRequest'
        description: Revoke payload
        required: true
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Revoke a pack
      tags:
        - authority
  /orgs/{orgId}/authority/packs/{packId}/school-review:
    post:
      description: |-
        Attaches (or clears, when clear=true) a school-level review
        attestation to a pack. This is operational — it is NOT an
        authority/regulatory approval. The public verify page
        renders this as a distinct trust badge.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Evidence pack ID
          in: path
          name: packId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.packSchoolReviewRequest'
        description: Review payload
        required: true
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: School-review a pack
      tags:
        - authority
  /orgs/{orgId}/authority/packs/{packId}/verify:
    post:
      description: Verifies the cryptographic integrity and signatures of an evidence pack.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Evidence Pack ID
          in: path
          name: packId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Verify evidence pack integrity
      tags:
        - authority
  /orgs/{orgId}/authority/public_key:
    get:
      description: Returns the public key used by the authority service to sign attestations and evidence packs.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: SERVICE_UNAVAILABLE
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get authority signing public key
      tags:
        - authority
  /orgs/{orgId}/automation/alerts:
    get:
      description: Returns automation alerts for the organization with optional status, severity, type, and entity_type filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by status (active, resolved, dismissed)
          in: query
          name: status
          schema:
            type: string
        - description: Filter by severity (info, warning, critical)
          in: query
          name: severity
          schema:
            type: string
        - description: Filter by alert type
          in: query
          name: type
          schema:
            type: string
        - description: Filter by entity type
          in: query
          name: entity_type
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset for pagination
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List automation alerts
      tags:
        - automation
  /orgs/{orgId}/automation/alerts/{id}/dismiss:
    patch:
      description: Marks an automation alert as dismissed.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Alert ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Dismiss an automation alert
      tags:
        - automation
  /orgs/{orgId}/automation/alerts/{id}/resolve:
    patch:
      description: Marks an automation alert as resolved.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Alert ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Resolve an automation alert
      tags:
        - automation
  /orgs/{orgId}/automation/alerts/run:
    post:
      description: Triggers a full alerts evaluation cycle for the organization and returns metrics.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Run alerts evaluation
      tags:
        - automation
  /orgs/{orgId}/automation/alerts/summary:
    get:
      description: Returns a severity breakdown (info, warning, critical) of active automation alerts.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get alerts summary
      tags:
        - automation
  /orgs/{orgId}/automation/events:
    get:
      description: Returns domain events processed by the v2 automation engine, with optional kind filter.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by event kind
          in: query
          name: kind
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List automation events (v2)
      tags:
        - automation
  /orgs/{orgId}/automation/events/{eventId}/retry:
    post:
      description: Re-processes a domain event through the v2 automation engine.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Retry automation event (v2)
      tags:
        - automation
  /orgs/{orgId}/automation/job_runs:
    get:
      description: Returns job run history for the organization, with optional status and date filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by status (started, success, failed)
          in: query
          name: status
          schema:
            type: string
        - description: Filter by job type
          in: query
          name: job_type
          schema:
            type: string
        - description: Start timestamp filter
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp filter
          in: query
          name: to
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List automation job runs
      tags:
        - automation
  /orgs/{orgId}/automation/outbox:
    get:
      description: Returns queued notification outbox entries with optional status filter.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by delivery status
          in: query
          name: status
          schema:
            type: string
        - description: Max results (default 50, max 200)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List notification outbox
      tags:
        - automation
  /orgs/{orgId}/automation/recipes:
    get:
      description: Returns the catalog of recipes with per-org enabled status and tier availability.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List automation recipe catalog
      tags:
        - automation
  /orgs/{orgId}/automation/recipes/{recipeId}/enable:
    delete:
      description: Soft-disables the cloned rule (preserves run history).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Recipe ID
          in: path
          name: recipeId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Disable a recipe for the organization
      tags:
        - automation
    post:
      description: Clones the recipe template into an automation_rule for the org. Idempotent.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Recipe ID
          in: path
          name: recipeId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: FORBIDDEN (tier gating)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: Recipe not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Enable a recipe for the organization
      tags:
        - automation
  /orgs/{orgId}/automation/rules:
    delete:
      description: Deletes an automation rule. Legacy endpoint kept for backward compatibility.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete automation rule (legacy)
      tags:
        - automation
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: List automation rules
      tags:
        - automation-rules
    patch:
      description: Patches an existing automation rule. Legacy endpoint kept for backward compatibility.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.automationRulePatchRequest'
        description: Automation rule patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update automation rule (legacy)
      tags:
        - automation
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.automationRulePayloadV1'
        description: Automation rule payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Create automation rule (v1)
      tags:
        - automation-rules
  /orgs/{orgId}/automation/rules/{ruleId}:
    delete:
      description: Soft-deletes an automation rule by marking it as deleted and disabled.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rule ID
          in: path
          name: ruleId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete automation rule (v1)
      tags:
        - automation
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rule ID
          in: path
          name: ruleId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Get automation rule
      tags:
        - automation-rules
    put:
      description: Updates an existing automation rule by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rule ID
          in: path
          name: ruleId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.automationRulePatchPayloadV1'
        description: Automation rule patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update automation rule (v1)
      tags:
        - automation
  /orgs/{orgId}/automation/rules/{ruleId}/toggle:
    post:
      description: Toggles an automation rule between active and disabled states.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rule ID
          in: path
          name: ruleId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.automationRuleToggleRequestV1'
        description: Toggle payload
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Toggle automation rule status
      tags:
        - automation
  /orgs/{orgId}/automation/rules/dry_run:
    post:
      description: Evaluates an automation rule against live data without executing actions. Returns match results and action previews.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.automationRuleDryRunRequestV1'
        description: Dry run request
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Dry-run automation rule
      tags:
        - automation
  /orgs/{orgId}/automation/rules/validate:
    post:
      description: Validates an automation rule payload without persisting it. Returns normalized rule and any issues.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.automationRuleValidateRequestV1'
        description: Rule payload to validate
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Validate automation rule
      tags:
        - automation
  /orgs/{orgId}/automation/run_now:
    post:
      description: Manually triggers an automation rule, event dispatch, or time-based rule execution.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.runAutomationNowRequest'
        description: Run now payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Trigger automation run immediately
      tags:
        - automation
  /orgs/{orgId}/automation/runs:
    get:
      description: Returns automation rule execution runs, with optional rule_id and event_id filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by rule ID
          in: query
          name: rule_id
          schema:
            type: string
        - description: Filter by event ID
          in: query
          name: event_id
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List automation runs (v2)
      tags:
        - automation
  /orgs/{orgId}/automation/templates:
    get:
      description: Returns all notification templates for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List notification templates
      tags:
        - automation
    post:
      description: Creates a new notification template for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.notificationTemplateCreateRequest'
        description: Template payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create notification template
      tags:
        - automation
  /orgs/{orgId}/automation/templates/{templateId}:
    patch:
      description: Patches an existing notification template.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template ID
          in: path
          name: templateId
          schema:
            type: string
          required: true
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.notificationTemplatePatchRequest'
        description: Template patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update notification template
      tags:
        - automation
  /orgs/{orgId}/bases:
    get:
      description: Returns all bases for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.baseResponse'
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: SERVER_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List bases
      tags:
        - bases
    post:
      description: Adds a new base to the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.createBaseRequest'
        description: Base payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.baseResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a base
      tags:
        - bases
  /orgs/{orgId}/bases/{baseId}:
    delete:
      description: Deletes a base. The primary base cannot be deleted.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Base ID
          in: path
          name: baseId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete a base
      tags:
        - bases
    put:
      description: Updates name, ICAO/IATA codes, or timezone of a base.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Base ID
          in: path
          name: baseId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.updateBaseRequest'
        description: Fields to update
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.baseResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update a base
      tags:
        - bases
  /orgs/{orgId}/bases/{baseId}/set-primary:
    post:
      description: Changes which base is the primary for the organization. The previous primary is demoted atomically.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Base ID
          in: path
          name: baseId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.baseResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Set primary base
      tags:
        - bases
  /orgs/{orgId}/benchmarks/opt_in:
    patch:
      description: Enables or disables benchmark data sharing for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Toggle benchmark opt-in
      tags:
        - benchmarks
  /orgs/{orgId}/benchmarks/summary:
    get:
      description: Returns the organization's benchmark metrics compared against the opted-in cohort for a given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Range in days (e.g. 30, 60, 90)
          in: query
          name: range
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get benchmark summary
      tags:
        - benchmarks
  /orgs/{orgId}/billing/checkout:
    post:
      description: Creates a Stripe Checkout session for the organization to subscribe to a plan.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.billingCheckoutRequest'
        description: Checkout payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: SERVICE_UNAVAILABLE
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create billing checkout session
      tags:
        - billing
  /orgs/{orgId}/billing/invoices:
    get:
      description: Returns Stripe invoices for the organization, falling back to live Stripe API when the database is empty.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results (1-100, default 50)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '502':
          description: BAD_GATEWAY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List billing invoices
      tags:
        - billing
  /orgs/{orgId}/billing/portal:
    post:
      description: Creates a Stripe Customer Portal session so the organization can manage their subscription.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.billingPortalRequest'
        description: Portal payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: SERVICE_UNAVAILABLE
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create billing portal session
      tags:
        - billing
  /orgs/{orgId}/billing/status:
    get:
      description: Returns the current billing status including Stripe customer, subscription, and entitlements for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get billing status
      tags:
        - billing
  /orgs/{orgId}/capabilities:
    get:
      description: Returns module availability for a specific org using schema presence and feature flags.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.capabilitiesResponse'
      summary: Get organization capabilities
      tags:
        - capabilities
  /orgs/{orgId}/charges:
    get:
      description: Returns a paginated list of charges for the organization. Supports filtering by student, date range, and status.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List charges
      tags:
        - finance
    post:
      description: Records a new charge (fee, expense, or cost) against a student or enrollment. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.ChargeCreateDTO'
        description: Charge payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Create charge
      tags:
        - finance
  /orgs/{orgId}/charges/{chargeId}:
    delete:
      description: Soft-deletes a charge record. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Charge ID
          in: path
          name: chargeId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete charge
      tags:
        - finance
    get:
      description: Returns a single charge record by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Charge ID
          in: path
          name: chargeId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get charge
      tags:
        - finance
    patch:
      description: Partially updates an existing charge record. Only supplied fields are changed. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Charge ID
          in: path
          name: chargeId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.chargePatchDTO'
        description: Charge patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update charge
      tags:
        - finance
  /orgs/{orgId}/charges/{chargeId}/status:
    patch:
      description: Updates only the status field of an existing charge. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Charge ID
          in: path
          name: chargeId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.chargePatchDTO'
        description: Charge status payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update charge status
      tags:
        - finance
  /orgs/{orgId}/compliance-report:
    get:
      description: Returns a compliance forensic report with evaluation totals, blocked reason codes, and recent blocked entries over a configurable window.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Window in days (default 7, max 90)
          in: query
          name: days
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get compliance forensic report
      tags:
        - compliance
  /orgs/{orgId}/compliance/calendar:
    get:
      description: |-
        Returns a priority-ranked queue of compliance entries:
        expiring records, expired records, items missing for people
        whose role requires them, and failing currency rules. Includes
        summary stats (by state, severity, category). Honest: "missing"
        means no record is tracked for a required item+role, not that
        the authority deems the person non-compliant.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: ISO date (YYYY-MM-DD) — only entries with due_date >= this
          in: query
          name: from
          schema:
            type: string
        - description: ISO date (YYYY-MM-DD) — only entries with due_date <= this
          in: query
          name: to
          schema:
            type: string
        - description: Category filter (license, rating, medical, document, currency)
          in: query
          name: category
          schema:
            type: string
        - description: State filter (ok, due_soon, due, expired, missing, review_required, handled, currency_failing)
          in: query
          name: state
          schema:
            type: string
        - description: Severity filter (critical, high, medium, low)
          in: query
          name: severity
          schema:
            type: string
        - description: Person type (student, pilot, instructor, staff)
          in: query
          name: person_type
          schema:
            type: string
        - description: Person UUID
          in: query
          name: person_id
          schema:
            type: string
        - description: Person name/email search
          in: query
          name: search
          schema:
            type: string
        - description: Include ok entries (default false)
          in: query
          name: include_ok
          schema:
            type: string
        - description: Include handled entries (default false)
          in: query
          name: include_handled
          schema:
            type: string
        - description: Max entries (default 200, max 500)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.complianceCalendarResult'
      security:
        - BearerAuth: []
      summary: Consolidated compliance calendar
      tags:
        - compliance
  /orgs/{orgId}/compliance/calendar.ics:
    get:
      description: |-
        Returns every renewal in the next 90 days for the org as
        an iCalendar feed. Auth-gated by compliance.view. Suitable
        for one-shot download or for hosting in a shared admin
        calendar (Google Calendar / Outlook 365 group).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/calendar:
              schema:
                type: string
                format: binary
      security:
        - BearerAuth: []
      summary: Org-wide compliance calendar (.ics)
      tags:
        - compliance
  /orgs/{orgId}/compliance/calendar.pdf:
    get:
      description: |-
        Renders the next 90 days of compliance entries as a single
        A4 PDF, grouped by month. Internal reference — not a
        substitute for official authority records.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/pdf:
              schema:
                type: string
                format: binary
      security:
        - BearerAuth: []
      summary: Org-wide compliance calendar (PDF)
      tags:
        - compliance
  /orgs/{orgId}/compliance/calendar/handle:
    delete:
      description: |-
        Removes any handling overlay for the (person, item) pair.
        The underlying compliance record is untouched.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.complianceCalendarHandleDeleteRequest'
        description: Target payload
        required: true
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Clear a handling overlay
      tags:
        - compliance
    post:
      description: |-
        Upserts a handling overlay for a (person, compliance_item)
        pair. The underlying record status is NEVER mutated. Handling
        state is one of: reviewed, snoozed, handled. snooze_until is
        required when state=snoozed.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.complianceCalendarHandleRequest'
        description: Handling payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.complianceCalendarHandlingSummary'
      security:
        - BearerAuth: []
      summary: Acknowledge / snooze / mark-handled a compliance entry
      tags:
        - compliance
  /orgs/{orgId}/compliance/currency/rules:
    get:
      description: Returns all currency rules configured for the organization. Currency rules define recency requirements (e.g., 3 landings in 90 days).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.currencyRuleResponse'
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List currency rules
      tags:
        - compliance
  /orgs/{orgId}/compliance/currency/rules/{ruleId}:
    delete:
      description: Deletes a currency rule by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Currency Rule ID
          in: path
          name: ruleId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete currency rule
      tags:
        - compliance
    get:
      description: Returns a single currency rule by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Currency Rule ID
          in: path
          name: ruleId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get currency rule
      tags:
        - compliance
  /orgs/{orgId}/compliance/dashboard:
    get:
      description: Returns an aggregated compliance overview including counts by status, expiring-soon items, and expired items across all tracked people.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person type filter
          in: query
          name: type
          schema:
            type: string
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
        - description: Search by name
          in: query
          name: search
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Compliance dashboard
      tags:
        - compliance
  /orgs/{orgId}/compliance/digest/run:
    post:
      description: |-
        Triggers an immediate digest send for the current ISO
        week. Idempotent: if any (recipient, kind) row already
        exists for this Monday-anchored week, the corresponding
        recipient is skipped. Useful when an admin wants to test
        the email content or push the digest mid-week after a
        compliance update.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Manually flush the weekly compliance digest
      tags:
        - compliance
  /orgs/{orgId}/compliance/digest/runs:
    get:
      description: |-
        Returns the most recent digest runs for the org
        (default limit 50). Useful for the admin UI to render
        "last sent / next scheduled". Requires compliance.view.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max rows (default 50, max 200)
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Recent compliance digest runs
      tags:
        - compliance
  /orgs/{orgId}/compliance/export:
    get:
      description: Exports compliance records for all tracked people as a downloadable CSV file. Supports the same type/status/search filters as the dashboard.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person type filter
          in: query
          name: type
          schema:
            type: string
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
        - description: Search by name
          in: query
          name: search
          schema:
            type: string
      responses:
        '200':
          description: CSV file download
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '400':
          description: VALIDATION_ERROR
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export compliance data as CSV
      tags:
        - compliance
  /orgs/{orgId}/compliance/items:
    get:
      description: Returns the compliance item catalog for the organization. Supports optional category and search filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by category
          in: query
          name: category
          schema:
            type: string
        - description: Search by code or name
          in: query
          name: search
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.complianceItemResponse'
                type: array
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List compliance items
      tags:
        - compliance
    post:
      description: Adds a new compliance item to the organization catalog. Name is required; code is auto-generated if omitted. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.complianceItemCreateRequest'
        description: Compliance item payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.complianceItemResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create compliance item
      tags:
        - compliance
  /orgs/{orgId}/compliance/items/{itemId}:
    delete:
      description: Deletes a compliance item from the organization's catalog. Fails if the item is in use.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Compliance Item ID
          in: path
          name: itemId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete compliance item
      tags:
        - compliance
    get:
      description: Returns a single compliance item by ID from the organization's catalog.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Compliance Item ID
          in: path
          name: itemId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get compliance item
      tags:
        - compliance
  /orgs/{orgId}/compliance/people:
    get:
      description: Returns people tracked in the compliance module, with optional type, status, and search filters. Supports limit/offset pagination.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person type filter
          in: query
          name: type
          schema:
            type: string
        - description: Status filter
          in: query
          name: status
          schema:
            type: string
        - description: Search by name
          in: query
          name: search
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.compliancePersonResponse'
                type: array
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List compliance people
      tags:
        - compliance
  /orgs/{orgId}/compliance/people/{personId}/currency/events:
    get:
      description: Returns currency events scoped to a specific person.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: path
          name: personId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List currency events for person
      tags:
        - compliance
    post:
      description: Creates a currency event scoped to a specific person.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: path
          name: personId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create currency event for person
      tags:
        - compliance
  /orgs/{orgId}/compliance/people/{personId}/export:
    get:
      description: Exports compliance records and evaluation for a specific person as a CSV file.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: path
          name: personId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export person compliance as CSV
      tags:
        - compliance
  /orgs/{orgId}/compliance/people/{personId}/records/{recordId}:
    delete:
      description: Deletes a compliance record scoped to a specific person.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: path
          name: personId
          required: true
          schema:
            type: string
        - description: Record ID
          in: path
          name: recordId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete person compliance record (by person path)
      tags:
        - compliance
    get:
      description: Returns a compliance record for a specific person by person and record IDs.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: path
          name: personId
          required: true
          schema:
            type: string
        - description: Record ID
          in: path
          name: recordId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get person compliance record
      tags:
        - compliance
    patch:
      description: Updates a compliance record scoped to a specific person.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: path
          name: personId
          required: true
          schema:
            type: string
        - description: Record ID
          in: path
          name: recordId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update person compliance record (by person path)
      tags:
        - compliance
  /orgs/{orgId}/compliance/people/{personId}/records/{recordId}/renewals:
    post:
      description: |-
        Creates a new person_compliance_records row with
        review_state=pending_review and supersedes_id pointing at
        the source record. The source row is NOT modified until
        the renewal is approved. Renewals are limited to the
        medical/license/rating categories. Evidence is optional
        and captured as a reference only — the backend does not
        validate documents.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person UUID
          in: path
          name: personId
          required: true
          schema:
            type: string
        - description: Source record UUID (the one being renewed)
          in: path
          name: recordId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.renewalCreateRequest'
        description: Renewal payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.renewalRecordResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Initiate a renewal for a compliance record
      tags:
        - compliance
  /orgs/{orgId}/compliance/records:
    get:
      description: Returns compliance records filtered by person_id query parameter. Compatibility alias.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Person ID
          in: query
          name: person_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List compliance records by person query
      tags:
        - compliance
  /orgs/{orgId}/compliance/records/{recordId}:
    get:
      description: Returns a single person compliance record by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Record ID
          in: path
          name: recordId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get compliance record
      tags:
        - compliance
  /orgs/{orgId}/compliance/records/{recordId}/history:
    get:
      description: |-
        Returns every record linked to this record's chain —
        predecessors and successors — ordered oldest-first.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Any record in the chain
          in: path
          name: recordId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.renewalRecordResponse'
                type: array
      security:
        - BearerAuth: []
      summary: Return the supersession chain for a record
      tags:
        - compliance
  /orgs/{orgId}/compliance/records/{recordId}/review:
    post:
      description: |-
        Applies the review decision to a renewal record. On
        approval the source record is superseded (status=expired,
        superseded_at set) and any handling overlay for (person,
        item) is cleared. The source record is NEVER deleted.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Renewal record UUID (the pending one)
          in: path
          name: recordId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.renewalReviewRequest'
        description: Review decision
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.renewalRecordResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Approve or reject a pending renewal
      tags:
        - compliance
  /orgs/{orgId}/copilot/actions/run:
    post:
      description: Executes a named copilot action with the provided payload and records an audit log entry.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Run a copilot action
      tags:
        - copilot
  /orgs/{orgId}/copilot/chat:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.copilotChatRequest'
        description: Chat request payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Run Copilot chat
      tags:
        - copilot
  /orgs/{orgId}/copilot/context:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Get Copilot context
      tags:
        - copilot
  /orgs/{orgId}/copilot/threads:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: List Copilot threads
      tags:
        - copilot
  /orgs/{orgId}/copilot/threads/{id}/messages:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Thread ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: List Copilot thread messages
      tags:
        - copilot
  /orgs/{orgId}/daily-metrics:
    get:
      description: Returns daily operational metrics for the organization within the given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start date (YYYY-MM-DD)
          in: query
          name: date_from
          required: true
          schema:
            type: string
        - description: End date (YYYY-MM-DD)
          in: query
          name: date_to
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List daily metrics
      tags:
        - intelligence
  /orgs/{orgId}/dashboard/ops-home:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Recent activity items limit (1-20)
          in: query
          name: activity_limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Operational dashboard home summary
      tags:
        - dashboard
  /orgs/{orgId}/dashboard/summary:
    get:
      description: Returns a high-level summary for the organization dashboard including student counts, flight hours, and revenue metrics.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Dashboard summary
      tags:
        - dashboard
  /orgs/{orgId}/developer-apps:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List developer apps
      tags:
        - platform-developer-apps
    post:
      description: Creates a draft developer app owned by the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.DeveloperApp'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a developer app
      tags:
        - platform-developer-apps
  /orgs/{orgId}/developer-apps/{appId}:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.DeveloperApp'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get a developer app
      tags:
        - platform-developer-apps
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.DeveloperApp'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update a developer app
      tags:
        - platform-developer-apps
  /orgs/{orgId}/developer-apps/{appId}/activate:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/platform.DeveloperApp'
        '401':
          description: UNAUTHORIZED
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Activate a developer app (draft -> active)
      tags:
        - platform-developer-apps
  /orgs/{orgId}/developer-apps/{appId}/listings:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List addon listings for a developer app
      tags:
        - platform-addon-listings
    post:
      description: |-
        Creates a draft addon listing owned by the developer_app. The
        listing starts in status='private', review_status='not_submitted'.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an addon listing (draft)
      tags:
        - platform-addon-listings
  /orgs/{orgId}/developer-apps/{appId}/listings/{listingId}:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
        - description: Addon listing ID
          in: path
          name: listingId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get an addon listing
      tags:
        - platform-addon-listings
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
        - description: Addon listing ID
          in: path
          name: listingId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update an addon listing (partial)
      tags:
        - platform-addon-listings
  /orgs/{orgId}/developer-apps/{appId}/listings/{listingId}/submit:
    post:
      description: |-
        Runs the 11 automated listing checks in a single tx. Transitions
        the listing to either pending_review (all block-severity checks
        passed) or automated_checks_failed (at least one block failure).
        Re-submitting from pending_review or approved returns 409.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
        - description: Addon listing ID
          in: path
          name: listingId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AddonListing'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Submit a listing for review — runs 11 automated checks
      tags:
        - platform-addon-listings
  /orgs/{orgId}/developer-apps/{appId}/revoke:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/platform.DeveloperApp'
        '401':
          description: UNAUTHORIZED
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke a developer app (active -> revoked) - cascades to api_keys
      tags:
        - platform-developer-apps
  /orgs/{orgId}/developer-apps/{appId}/usage:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Developer app ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.UsageSummary'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Usage stats for a developer app
      tags:
        - platform-developer-apps
  /orgs/{orgId}/enrollments:
    get:
      description: Returns student enrollments, optionally filtered by status, student, or program.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Enrollment status filter
          in: query
          name: status
          schema:
            type: string
        - description: Student ID filter
          in: query
          name: student_id
          schema:
            type: string
        - description: Program ID filter
          in: query
          name: program_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List enrollments
      tags:
        - students
    post:
      description: Enrolls a student in a training program.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.enrollmentUpsertRequest'
        description: Enrollment payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create enrollment
      tags:
        - students
  /orgs/{orgId}/enrollments/{enrollmentId}:
    get:
      description: Returns a single student enrollment by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Enrollment ID
          in: path
          name: enrollmentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get enrollment
      tags:
        - students
    patch:
      description: Partially updates a student enrollment.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Enrollment ID
          in: path
          name: enrollmentId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.enrollmentUpsertRequest'
        description: Enrollment patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update enrollment
      tags:
        - students
  /orgs/{orgId}/enrollments/{enrollmentId}/milestones/{milestoneId}:
    patch:
      description: Updates the status of a specific milestone within an enrollment.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Enrollment ID
          in: path
          name: enrollmentId
          required: true
          schema:
            type: string
        - description: Milestone ID
          in: path
          name: milestoneId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update enrollment milestone
      tags:
        - students
  /orgs/{orgId}/enrollments/{enrollmentId}/status:
    patch:
      description: Transitions an enrollment to a new status with validation of allowed transitions.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Enrollment ID
          in: path
          name: enrollmentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update enrollment status
      tags:
        - students
  /orgs/{orgId}/events:
    get:
      description: Returns integration events for the organization, with optional type and date filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event type filter
          in: query
          name: type
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: since
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: until
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List integration events
      tags:
        - integrations
  /orgs/{orgId}/events/{eventId}/replay:
    post:
      description: Re-dispatches an integration event by ID, creating a new event linked to the original.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Replay an integration event
      tags:
        - integrations
  /orgs/{orgId}/finance/balances.csv:
    get:
      description: Downloads a CSV file containing per-student balance summaries for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export finance balances as CSV
      tags:
        - finance
  /orgs/{orgId}/finance/customers/{customerId}:
    patch:
      description: Resolves billing_customers.{customerId} → stripe_customer_id and
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Internal billing_customers UUID
          in: path
          name: customerId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.customerUpdateRequest'
        description: Customer update payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR / empty_patch
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED (mfa step missing/stale)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN (missing finance.customers.write)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND (unknown customer in this org)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '502':
          description: BAD_GATEWAY (stripe upstream)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: SERVICE_UNAVAILABLE (circuit open)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update an org's Stripe customer record
      tags:
        - finance
  /orgs/{orgId}/finance/dashboard:
    get:
      description: Returns the finance dashboard view with aggregated balances, recent transactions, and monthly totals for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Month token (YYYY-MM)
          in: query
          name: month
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Finance dashboard
      tags:
        - finance
  /orgs/{orgId}/finance/drift:
    get:
      description: Returns drift audit_log rows from the last N days (default 30,
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Lookback window in days (1-90, default 30)
          in: query
          name: days
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR (invalid_days)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '403':
          description: FORBIDDEN (missing finance.drift.review)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List unresolved Stripe-reconciliation drift entries
      tags:
        - finance
  /orgs/{orgId}/finance/drift/{auditLogId}/acknowledge:
    post:
      description: Marks a single drift audit_log row as acknowledged by the
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Source audit_log UUID
          in: path
          name: auditLogId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.driftAckRequest'
        description: Optional acknowledgement note (≤ 500 chars, no newlines)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR (note_invalid / note_too_long / invalid_audit_log_id)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED (mfa step missing/stale)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN (missing finance.drift.review)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND (drift_not_found)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Acknowledge a Stripe drift entry
      tags:
        - finance
  /orgs/{orgId}/finance/dunning/overdue:
    get:
      description: Dashboard feed. Joins invoice_receivables with dunning columns and resolves each row's next recommended action (send reminder N, pause, suggest SEPA retry, escalate, none).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: 'Limit to invoices whose due_on has passed (default: false — returns every open/partial invoice)'
          in: query
          name: overdue_only
          schema:
            type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List invoices with dunning state + next action
      tags:
        - finance
  /orgs/{orgId}/finance/expense-categories:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Include archived categories
          in: query
          name: include_archived
          schema:
            type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List expense categories
      tags:
        - finance
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.expenseCategoryCreateDTO'
        description: Category payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Create expense category
      tags:
        - finance
  /orgs/{orgId}/finance/expense-categories/{categoryId}:
    delete:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Category ID
          in: path
          name: categoryId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
          content:
            application/json:
              schema:
                type: string
      security:
        - BearerAuth: []
      summary: Archive (soft-delete) expense category. System rows reject hard delete.
      tags:
        - finance
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Category ID
          in: path
          name: categoryId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Patch expense category
      tags:
        - finance
  /orgs/{orgId}/finance/exports/datev.csv:
    get:
      description: |-
        Stream a DATEV-importable EXTF Buchungsstapel CSV for a given fiscal period.
        Available to orgs whose country pack supports DATEV (currently Germany via the de pack).
        Country gating is enforced by the pack registry — the handler is jurisdiction-agnostic.
        Permission required : reports.access. Tier required : Operations+.
      parameters:
        - description: Organization ID (UUID)
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Period start (YYYY-MM-DD)
          in: query
          name: from
          schema:
            type: string
        - description: Period end (YYYY-MM-DD)
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: DATEV CSV stream (text/csv; charset=utf-8)
          content:
            text/csv:
              schema:
                type: string
        '400':
          description: BAD_REQUEST
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN or feature_not_entitled
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export DATEV (Buchungsstapel CSV)
      tags:
        - finance
  /orgs/{orgId}/finance/exports/fec.txt:
    get:
      description: |-
        Stream the DGFiP-compliant FEC file for a given fiscal period.
        Format : article A47 A-1 LPF, 18 columns pipe-separated, UTF-8 with CRLF.
        Available only to orgs whose country pack supports FEC (currently France).
        Permission required : reports.access. Tier required : Operations+.
      parameters:
        - description: Organization ID (UUID)
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Period start (YYYY-MM-DD)
          in: query
          name: from
          schema:
            type: string
        - description: Period end (YYYY-MM-DD)
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: FEC stream (text/plain; charset=utf-8)
          content:
            text/plain:
              schema:
                type: string
        '400':
          description: BAD_REQUEST
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN or feature_not_entitled
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export FEC (Fichier des Écritures Comptables)
      tags:
        - finance
  /orgs/{orgId}/finance/exports/saft.xml:
    get:
      description: |-
        Stream the SAF-T XML file for a given fiscal period.
        Available to orgs whose country pack supports SAF-T (currently PT, PL, NO, LU, HU).
        Country gating is enforced by the pack registry — the handler is jurisdiction-agnostic.
        Permission required : reports.access. Tier required : Operations+.
      parameters:
        - description: Organization ID (UUID)
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Period start (YYYY-MM-DD)
          in: query
          name: from
          schema:
            type: string
        - description: Period end (YYYY-MM-DD)
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: SAF-T XML stream (application/xml; charset=utf-8)
          content:
            text/xml:
              schema:
                type: string
        '400':
          description: BAD_REQUEST
          content:
            text/xml:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            text/xml:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN or feature_not_entitled
          content:
            text/xml:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL
          content:
            text/xml:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export SAF-T (Standard Audit File for Tax)
      tags:
        - finance
  /orgs/{orgId}/finance/forecast:
    get:
      description: Returns the financial forecast for the organization with projected monthly revenue, costs, and net cashflow.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Forecast base month (YYYY-MM or YYYY-MM-DD)
          in: query
          name: as_of_month
          schema:
            type: string
        - description: Number of months to forecast
          in: query
          name: horizon
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get finance forecast
      tags:
        - finance
  /orgs/{orgId}/finance/forecast/recompute:
    post:
      description: Forces a recomputation of the financial forecast and returns the updated projection.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Forecast base month (YYYY-MM or YYYY-MM-DD)
          in: query
          name: as_of_month
          schema:
            type: string
        - description: Number of months to forecast
          in: query
          name: horizon
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Recompute finance forecast
      tags:
        - finance
  /orgs/{orgId}/finance/invoices/{invoiceId}/refund:
    post:
      description: Resolves the Stripe payment_intent associated with the
      parameters:
        - description: Organization UUID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Internal invoice UUID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.refundRequest'
        description: Refund payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED (mfa step missing/stale)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN (missing finance.refunds.execute)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE (no completed student_payment)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '502':
          description: BAD_GATEWAY (stripe upstream error)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: SERVICE_UNAVAILABLE (circuit open)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Issue a Stripe refund for a student invoice
      tags:
        - finance
  /orgs/{orgId}/finance/invoices/receivables:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Comma-separated payment_state filter (open,partial,paid,overpaid,void)
          in: query
          name: states
          schema:
            type: string
        - description: Filter to a single student
          in: query
          name: student_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List reconcilable invoice receivables
      tags:
        - finance
  /orgs/{orgId}/finance/payments/unapplied:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List completed payments with no invoice allocation
      tags:
        - finance
  /orgs/{orgId}/finance/sepa/collections:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by status
          in: query
          name: status
          schema:
            type: string
        - description: Filter by mandate
          in: query
          name: mandate_id
          schema:
            type: string
        - description: Filter by invoice
          in: query
          name: invoice_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List SEPA collections
      tags:
        - sepa
    post:
      description: Creates a collection in status=draft, validates eligibility against the mandate and invoice, then calls the configured provider to move to submitted.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.sepaCollectionCreateDTO'
        description: Collection payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Initiate a SEPA direct debit collection
      tags:
        - sepa
  /orgs/{orgId}/finance/sepa/collections/{collectionId}/status:
    patch:
      description: Runs the state machine and, on status=paid, materialises a payment (method=sepa_direct_debit) auto-allocated to the invoice. Non-paid transitions never touch payments.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Collection ID
          in: path
          name: collectionId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Transition a collection to a new status
      tags:
        - sepa
  /orgs/{orgId}/finance/sepa/mandates:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by status
          in: query
          name: status
          schema:
            type: string
        - description: Filter by student
          in: query
          name: student_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List SEPA mandates
      tags:
        - sepa
    post:
      description: Captures an authorisation to debit a specific debtor account. No provider is called; the mandate starts in status=draft until staff activates it.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.sepaMandateCreateDTO'
        description: Mandate payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Create a SEPA direct debit mandate
      tags:
        - sepa
  /orgs/{orgId}/finance/sepa/mandates/{mandateId}:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Mandate ID
          in: path
          name: mandateId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get one SEPA mandate (full IBAN visible)
      tags:
        - sepa
    patch:
      description: Partial update. Transitioning status to revoked stamps revoked_at; to active stamps signed_at if not already set.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Mandate ID
          in: path
          name: mandateId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Update a SEPA mandate
      tags:
        - sepa
  /orgs/{orgId}/finance/settings:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Get finance settings for org
      tags:
        - finance
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Patch finance settings for org
      tags:
        - finance
  /orgs/{orgId}/finance/summary:
    get:
      description: Returns an aggregated financial summary for the organization, optionally scoped to a specific month.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Month token (YYYY-MM)
          in: query
          name: month
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Finance summary
      tags:
        - finance
  /orgs/{orgId}/flights:
    get:
      description: Returns paginated flights for the organization with optional status, date-range, and resource filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight status
          in: query
          name: status
          schema:
            type: string
        - description: Start date (RFC3339 or YYYY-MM-DD)
          in: query
          name: date_from
          schema:
            type: string
        - description: End date (RFC3339 or YYYY-MM-DD)
          in: query
          name: date_to
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
        - description: Limit
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: Sort field
          in: query
          name: sort
          schema:
            type: string
        - description: Sort order
          in: query
          name: order
          schema:
            type: string
        - description: Pagination cursor
          in: query
          name: cursor
          schema:
            type: string
        - description: Student ID filter
          in: query
          name: student_id
          schema:
            type: string
        - description: Instructor ID filter
          in: query
          name: instructor_id
          schema:
            type: string
        - description: Instructor person ID filter (alias; filters flights.instructor_id)
          in: query
          name: instructor_person_id
          schema:
            type: string
        - description: Aircraft ID filter
          in: query
          name: aircraft_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List flights
      tags:
        - flights
    post:
      description: Records a new flight for the organization. Runs compliance policy checks before persisting. Requires instructor or admin role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.FlightCreateDTO'
        description: Flight payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN or COMPLIANCE_BLOCKED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiComplianceBlockedErrorResponse'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create flight
      tags:
        - flights
  /orgs/{orgId}/flights/{flightId}:
    patch:
      description: Partially updates a flight record. Runs compliance policy checks on the updated state. Only supplied fields are changed.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.flightPatchDTO'
        description: Flight patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN or COMPLIANCE_BLOCKED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiComplianceBlockedErrorResponse'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update flight
      tags:
        - flights
  /orgs/{orgId}/flights/{flightId}/approve:
    post:
      description: Transitions a flight to the approved status. Typically called by an instructor or admin after review.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Approve flight
      tags:
        - flights
  /orgs/{orgId}/forecast-summary:
    get:
      description: Returns a combined forecast summary including cashflow projections, maintenance outlook, and instructor overload signals.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Forecast base month (YYYY-MM)
          in: query
          name: as_of_month
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get forecast summary
      tags:
        - intelligence
  /orgs/{orgId}/instructors:
    post:
      description: Adds a new instructor to the organization. First name and last name are required. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
        description: Instructor payload (first_name, last_name, email, phone, license_number, ratings, instructor_since, status, notes)
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT — duplicate email
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create instructor
      tags:
        - instructors
  /orgs/{orgId}/instructors/{instructorId}:
    delete:
      description: Soft-deletes an instructor. Blocked if the instructor has active students assigned. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Instructor ID
          in: path
          name: instructorId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT — active students assigned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete instructor
      tags:
        - instructors
    get:
      description: Returns a single instructor by ID, including active-student count and hours-this-month stats.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Instructor ID
          in: path
          name: instructorId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get instructor
      tags:
        - instructors
    patch:
      description: Partially updates an instructor record. Only supplied fields are changed. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Instructor ID
          in: path
          name: instructorId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
        description: Fields to update (phone, license_number, ratings, status, notes, email, first_name, last_name)
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update instructor
      tags:
        - instructors
  /orgs/{orgId}/integrations/api-keys:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List platform API keys for an organization
      tags:
        - platform
    post:
      description: The raw token is returned once in the response and never stored in plaintext. The caller is responsible for storing it in a secret manager.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Mint a new API key for the organization.
      tags:
        - platform
  /orgs/{orgId}/integrations/api-keys/{keyId}/revoke:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: API Key ID
          in: path
          name: keyId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: Revoke an API key.
      tags:
        - platform
  /orgs/{orgId}/integrations/apps:
    get:
      description: Returns the global app registry filtered to entries the caller's org should see (listed, beta, plus deprecated rows the org already installed). Each row carries an installable flag computed against the registry status.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List apps visible to the org
      tags:
        - app-store
  /orgs/{orgId}/integrations/apps/{slug}:
    get:
      description: Returns the registry entry by slug plus the installation row for the caller's org when it exists. Hidden statuses (draft / internal) return 404 unless the caller's org has an install row for them.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.appDetailResponse'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get one app + the calling org's installation if any.
      tags:
        - app-store
  /orgs/{orgId}/integrations/apps/{slug}/install:
    post:
      description: 'Idempotent: re-installing returns the existing row, re-enabling a disabled install flips status back to "installed", and a previously uninstalled row cannot be reinstalled (create a new row instead).'
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.installAppRequestPayload'
        description: Install payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.appInstallationResponse'
      security:
        - BearerAuth: []
      summary: Install an app for the calling org
      tags:
        - app-store
  /orgs/{orgId}/integrations/apps/{slug}/transition:
    post:
      description: 'Allowed transitions: installed→disabled, installed→uninstalled, disabled→installed, disabled→uninstalled. uninstalled is terminal.'
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App Slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Move an installation between statuses
      tags:
        - app-store
  /orgs/{orgId}/integrations/event-catalog:
    get:
      description: Authoritative list of event types the console + UI may
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: Published catalog of outbound webhook event types.
      tags:
        - platform
  /orgs/{orgId}/integrations/installed:
    get:
      description: Returns every installation for the given org joined with addon_listings + developer_apps so each row carries listing_name, listing_slug, provider_name. Uninstalled rows are included (audit history); callers filter status client-side. Ordered by installed_at DESC.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/platform.InstallationRow'
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List addon installations for an organization
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/installed/{installationId}:
    get:
      description: Returns the installation joined with addon_listings + developer_apps. 404 (no leak) if the installation is owned by a different org or doesn't exist.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App installation ID
          in: path
          name: installationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.InstallationRow'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get a single addon installation for an org
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/installed/{installationId}/revoke:
    post:
      description: "Marks the install as uninstalled and cascades: linked api_key is revoked, linked webhook_endpoint is disabled with disabled_reason='install_revoked'. The install row is kept (status='uninstalled') so the audit trail stays discoverable. The optional `reason` field in the JSON body is recorded in the audit_log metadata as `revoked_reason` for downstream incident review. Body is optional — an empty/missing body still revokes successfully."
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App installation ID
          in: path
          name: installationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.AppInstallation'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: already_uninstalled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke an addon installation — cascades to api_key + webhook
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/installed/{installationId}/rotate-key:
    post:
      description: Issues a new api_key linked to the install, revokes the old one, writes a synchronous audit row in the same tx. The new plaintext (new_api_key_plaintext) is returned EXACTLY ONCE.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App installation ID
          in: path
          name: installationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.RotateAPIKeyResult'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: already_uninstalled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Rotate the api_key for an addon installation
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/installed/{installationId}/rotate-webhook-secret:
    post:
      description: Rotates the signing secret on the webhook_endpoint that was auto-provisioned during the install. Delegates to the same dual-secret window the standalone /integrations/webhooks/{id}/rotate-secret endpoint uses — old secret is demoted to a 72h hashed secondary. The new plaintext is returned ONCE.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: App installation ID
          in: path
          name: installationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.RotateInstallWebhookSecretResult'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: already_uninstalled or no_webhook_linked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Rotate the webhook signing secret for an addon installation
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/listings:
    get:
      description: Returns every status='public' + review_status='approved' listing. Joined with the owning developer_app so each row carries provider_name. Ordered by install_count DESC, reviewed_at DESC. Gated by `integrations.manage` (same permission as the install flow).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/platform.PublicListingRow'
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Browse public addon listings (consumer marketplace)
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/listings/{slug}:
    get:
      description: Returns one status='public' + review_status='approved' listing. 404 (no leak) if the slug is unknown OR the listing is not public+approved.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Addon listing slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.PublicListingRow'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get a single public addon listing by slug
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/listings/{slug}/install:
    post:
      description: Returns the scopes-with-rationale + audit_id the caller will echo back to confirm. No state mutation.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Addon listing slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.ConsentPayload'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Prepare consent payload for an addon listing install
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/listings/{slug}/install/confirm:
    post:
      description: Validates accepted_scopes vs listing.required_scopes; provisions per-(app, org) api_key + optional webhook_endpoint; writes synchronous audit row in the same tx. Plaintext credentials (api_key_plaintext, webhook_secret_plaintext) are returned EXACTLY ONCE. Idempotent on audit_id — a repeat confirm with the same audit_id returns 200 + the same installation_id but null plaintext fields.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Addon listing slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.ConfirmResult'
        '400':
          description: scope_consent_mismatch or invalid audit_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Confirm an addon install — atomic provision of credentials
      tags:
        - platform-install-flow
  /orgs/{orgId}/integrations/webhooks:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: List webhook endpoints for the organization.
      tags:
        - platform
    post:
      description: The signing secret is returned once on create; use it to verify the HMAC-SHA256 signature of incoming deliveries.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: Register a new webhook endpoint.
      tags:
        - platform
  /orgs/{orgId}/integrations/webhooks/{endpointId}:
    delete:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook Endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: Delete a webhook endpoint.
      tags:
        - platform
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook Endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: Update a webhook endpoint.
      tags:
        - platform
  /orgs/{orgId}/integrations/webhooks/{endpointId}/deliveries:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook Endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: List recent delivery attempts for a webhook endpoint.
      tags:
        - platform
  /orgs/{orgId}/integrations/webhooks/{endpointId}/ping:
    post:
      description: 'Honest "ping": uses the same signing and delivery path as a'
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook Endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '202':
          description: Accepted
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '502':
          description: Bad Gateway
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      summary: Send a synthetic webhook.test event to verify the endpoint.
      tags:
        - platform
  /orgs/{orgId}/integrations/webhooks/{endpointId}/rotate-secret:
    post:
      description: |-
        Mints a new primary secret, demotes the old one to a 72h
        SHA-256 secondary. The new plaintext is shown ONCE.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/platform.RotationResult'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Rotate the webhook signing secret (72h dual-secret grace window)
      tags:
        - platform-webhooks
  /orgs/{orgId}/integrations/webhooks/queue/stats:
    get:
      description: Returns counts of pending/processing/delivered_24h/failed_24h plus oldest pending age.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Get webhook queue stats
      tags:
        - integrations
  /orgs/{orgId}/intelligence/anomalies:
    get:
      description: Returns explicit anomalies with rule name, current/baseline
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/intelligence.AnomalyReport'
      security:
        - BearerAuth: []
      summary: Rule-based anomaly signals
      tags:
        - intelligence
  /orgs/{orgId}/intelligence/cohorts:
    get:
      description: Returns program-level cohort analytics (velocity, stalled
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/intelligence.CohortAnalytics'
      security:
        - BearerAuth: []
      summary: Training cohort analytics
      tags:
        - intelligence
  /orgs/{orgId}/intelligence/health:
    get:
      description: Returns a coverage-aware composite health index. Score is
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/intelligence.HealthIndex'
      security:
        - BearerAuth: []
      summary: Honest school health index
      tags:
        - intelligence
  /orgs/{orgId}/library/compliance-templates:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List compliance item templates (GA + this org)
      tags:
        - library
  /orgs/{orgId}/library/compliance-templates/{slug}/apply:
    post:
      description: Idempotent on (organization_id, source_template_id).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: already applied
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '201':
          description: created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Apply a compliance item template to the calling org
      tags:
        - library
  /orgs/{orgId}/library/currency-templates:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List currency rule templates (GA + this org)
      tags:
        - library
  /orgs/{orgId}/library/currency-templates/{slug}/apply:
    post:
      description: Idempotent on (organization_id, source_template_id).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: already applied
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '201':
          description: created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Apply a currency rule template to the calling org
      tags:
        - library
  /orgs/{orgId}/logbook:
    get:
      description: Returns a paginated list of all training flight entries across the organization, with optional student and flight type filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page (max 200)
          in: query
          name: page_size
          schema:
            type: integer
        - description: Filter by flight type
          in: query
          name: flight_type
          schema:
            type: string
        - description: Filter by student ID
          in: query
          name: student_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List organization logbook entries
      tags:
        - logbook
  /orgs/{orgId}/logbook/totals:
    get:
      description: Returns aggregated hour totals for the organization logbook, with optional student and flight type filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by flight type
          in: query
          name: flight_type
          schema:
            type: string
        - description: Filter by student ID
          in: query
          name: student_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get organization logbook hour totals
      tags:
        - logbook
  /orgs/{orgId}/maintenance-programs/{programId}:
    delete:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Soft-delete a maintenance program
      tags:
        - maintenance
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Update a maintenance program
      tags:
        - maintenance
  /orgs/{orgId}/maintenance-programs/{programId}/apply:
    post:
      description: Creates a maintenance_due_items row for each program item that does not already have a matching open due item for the aircraft. Idempotent on title match.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            '*/*':
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Seed due items from a maintenance program
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/aircraft-flags:
    get:
      description: Returns grounded status and hard/soft due item counts per aircraft
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/app.aircraftFlagsDTO'
                type: array
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Per-aircraft maintenance flags for selector pre-submit signals
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/compliance-stats:
    get:
      description: Returns org-wide counts for grounded aircraft, hard-due overdue items,
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.complianceStatsDTO'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Maintenance compliance aggregate stats
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/due-items.csv:
    get:
      description: Downloads a CSV file of maintenance due items, optionally filtered by aircraft.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID filter
          in: query
          name: aircraft_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export maintenance due items as CSV
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/due_items:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: query
          name: aircraft_id
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
        - description: Limit
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: Sort field
          in: query
          name: sort
          schema:
            type: string
        - description: Sort order
          in: query
          name: order
          schema:
            type: string
        - description: Pagination cursor
          in: query
          name: cursor
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List maintenance due items
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/forecasts:
    get:
      description: Returns the latest maintenance forecasts for each due item, optionally filtered by aircraft and as-of date.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID filter
          in: query
          name: aircraft_id
          schema:
            type: string
        - description: As-of date filter (YYYY-MM-DD)
          in: query
          name: as_of_date
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List maintenance forecasts
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/work-orders.csv:
    get:
      description: Downloads a CSV file of maintenance work orders, optionally filtered by aircraft.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID filter
          in: query
          name: aircraft_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export maintenance work orders as CSV
      tags:
        - maintenance
  /orgs/{orgId}/maintenance/work_orders:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aircraft ID
          in: query
          name: aircraft_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiMaintenanceWorkOrdersListResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List maintenance work orders
      tags:
        - maintenance
  /orgs/{orgId}/marketplace/aircraft/booking_requests:
    post:
      description: The caller's org becomes the guest org. The host org is derived from the listing. Returns 422 with validation_warnings hints when a request is created against a degraded resource (overdue maintenance, overlap) — it does NOT block the request.
      parameters:
        - description: Guest (caller) organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.createBookingRequestPayload'
        description: Booking request payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.bookingRequestResponse'
      security:
        - BearerAuth: []
      summary: Submit a cross-org booking request
      tags:
        - marketplace-booking
  /orgs/{orgId}/marketplace/aircraft/booking_requests/{requestId}/transition:
    post:
      description: 'Host org actions: reviewing, approved, rejected. Guest org actions: cancelled. Approving creates a tentative planning_events row in the host org so the conflict-detection logic that already exists prevents double-booking; cancelling after approval also cancels the linked planning event.'
      parameters:
        - description: Caller organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Booking request ID
          in: path
          name: requestId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.transitionBookingPayload'
        description: Transition payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.bookingRequestResponse'
      security:
        - BearerAuth: []
      summary: Move a booking request through its state machine
      tags:
        - marketplace-booking
  /orgs/{orgId}/marketplace/aircraft/host/listings:
    put:
      description: Idempotent on (host_org, aircraft). Use status=published to make the listing visible to other orgs, status=paused to retract without deleting.
      parameters:
        - description: Host organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.upsertListingRequest'
        description: Listing payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.sharedListingResponse'
      security:
        - BearerAuth: []
      summary: Create or update a shared aircraft listing
      tags:
        - marketplace-booking
  /orgs/{orgId}/marketplace/aircraft/listings:
    get:
      description: Returns all published listings whose host org is NOT the caller's org. Use this to render the marketplace browse view.
      parameters:
        - description: Caller organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Browse cross-school aircraft listings
      tags:
        - marketplace-booking
  /orgs/{orgId}/monthly-snapshots:
    get:
      description: Returns pre-computed monthly financial snapshots for the organization. Useful for trend charts and period-over-period comparisons.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List monthly snapshots
      tags:
        - finance
  /orgs/{orgId}/monthly-snapshots/regenerate:
    post:
      description: Recomputes monthly financial snapshots for the organization. Use after bulk edits to payments or charges to refresh cached aggregates.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Regenerate monthly snapshots
      tags:
        - finance
  /orgs/{orgId}/notifications:
    get:
      description: Returns notifications for the authenticated user within the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by status (pending, sent, failed, read)
          in: query
          name: status
          schema:
            type: string
        - description: Filter by channel (email, in_app)
          in: query
          name: channel
          schema:
            type: string
        - description: Start timestamp filter
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp filter
          in: query
          name: to
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset for pagination
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List notifications
      tags:
        - automation
  /orgs/{orgId}/notifications/{notificationId}/read:
    patch:
      description: Marks a single in-app notification as read for the authenticated user.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Notification ID
          in: path
          name: notificationId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Mark notification as read
      tags:
        - automation
  /orgs/{orgId}/notifications/dev_sample:
    post:
      description: Sends a sample notification for development and testing purposes.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.devSampleNotificationRequest'
        description: Sample notification payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Send sample notification (dev)
      tags:
        - automation
  /orgs/{orgId}/notifications/mark_all_read:
    patch:
      description: Marks all unread in-app notifications as read for the authenticated user.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Mark all notifications as read
      tags:
        - automation
  /orgs/{orgId}/notifications/unread_count:
    get:
      description: Returns the count of unread in-app notifications for the authenticated user.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get unread notification count
      tags:
        - automation
  /orgs/{orgId}/onboarding/status:
    get:
      description: Returns a calculated onboarding progress for the organization, derived from real entity counts. Use this to drive the onboarding checklist UI.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: SERVER_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get onboarding status
      tags:
        - onboarding
  /orgs/{orgId}/ops/aerodrome_documents/{icao}:
    get:
      description: Returns global SIA-sourced references plus any org-specific overrides for the given ICAO. When the ICAO is not in our curated French registry the response is still 200 OK with availability_status="not_connected" so the UI can render an honest "no SIA reference" state.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Aerodrome ICAO code (e.g. LFPG)
          in: path
          name: icao
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.aerodromeDocumentsForICAOResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List aerodrome documents (VAC/IAC/AIP) for an ICAO
      tags:
        - operations
  /orgs/{orgId}/ops/briefings/flight/{flightId}:
    get:
      description: 'Returns a structured briefing bundle aggregating flight identity, route, aircraft, crew, weather, NOTAM, mass & balance, fuel, compliance, and overall readiness. Sections degrade honestly: each reports its own status and missing integrations (weather/NOTAM) are marked not_connected rather than filled with fake data.'
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get one-click pre-flight briefing bundle for a flight
      tags:
        - operations
  /orgs/{orgId}/ops/briefings/flight/{flightId}/card.html:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: HTML body
      security:
        - BearerAuth: []
      summary: Render the flight card HTML for in-browser preview / print
      tags:
        - operations
  /orgs/{orgId}/ops/briefings/flight/{flightId}/card.pdf:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: PDF body
      security:
        - BearerAuth: []
      summary: Download the flight card as a PDF
      tags:
        - operations
  /orgs/{orgId}/ops/briefings/flight/{flightId}/inputs:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.flightBriefingInputsDTO'
      security:
        - BearerAuth: []
      summary: Read the briefing inputs persisted for a flight
      tags:
        - operations
    put:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.flightBriefingInputsDTO'
        description: Inputs payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.flightBriefingInputsDTO'
      security:
        - BearerAuth: []
      summary: Set briefing inputs (occupants, fuel onboard, alternate, ...) for a flight
      tags:
        - operations
  /orgs/{orgId}/ops/day:
    get:
      description: "Single-page snapshot: today's flights grouped by ops_status, late returns, fleet status, instructor/student availability, weather, maintenance counters, pending approvals, briefing completeness."
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Date (YYYY-MM-DD) — defaults to today in org timezone
          in: query
          name: date
          schema:
            type: string
        - description: Timezone — defaults to Europe/Paris
          in: query
          name: tz
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dayBoardResponse'
      security:
        - BearerAuth: []
      summary: Day-of-Flight board snapshot
      tags:
        - dispatch
  /orgs/{orgId}/ops/fdm/ingests:
    post:
      description: Accepts a Garmin G1000 / G3X engine log CSV. The file is stored, classified, parsed inline and a fdm_flight + fdm_events are produced when parsing succeeds. Other formats are accepted but stored with status="unsupported" so the UI can show a clear message.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  description: FDM CSV file
                  type: string
                  contentMediaType: application/octet-stream
              required:
                - file
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.fdmIngestDetailResponse'
      security:
        - BearerAuth: []
      summary: Upload an FDM file for ingest
      tags:
        - fdm
  /orgs/{orgId}/ops/flights/{flightId}:
    delete:
      description: Soft-deletes a flight record. Requires instructor or admin role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete flight
      tags:
        - flights
    get:
      description: Returns a single flight record by ID, including linked student, instructor, and aircraft details.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get flight
      tags:
        - flights
  /orgs/{orgId}/ops/flights/{flightId}/acknowledge:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.acknowledgeRequest'
        description: Ack payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.acknowledgmentResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Record a flight acknowledgment (briefing/dispatch/return/debrief)
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/acknowledgments:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List acknowledgments for a flight
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/approval:
    post:
      description: Approves or rejects a flight based on the submitted decision. Used by instructors or admins during the review workflow.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.flightDecisionDTO'
        description: Approval decision payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Decide flight approval
      tags:
        - flights
  /orgs/{orgId}/ops/flights/{flightId}/cancel:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.dispatchTransitionRequest'
        description: Cancellation reason required
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '400':
          description: 'VALIDATION_ERROR: reason required'
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Cancel a flight
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/debrief:
    get:
      description: Returns the debrief record (or an empty draft if none exists yet) plus operationally useful signals (effects applied, logbook entry, open signoffs, FDM events).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.flightDebriefView'
      security:
        - BearerAuth: []
      summary: Read the post-flight debrief for a flight
      tags:
        - operations
    put:
      description: Creates or updates the debrief in draft state. Use the /complete endpoint to mark it as done.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/app.flightDebriefUpsertDTO'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.flightDebriefResponse'
      security:
        - BearerAuth: []
      summary: Upsert the post-flight debrief for a flight
      tags:
        - operations
  /orgs/{orgId}/ops/flights/{flightId}/debrief/complete:
    post:
      description: Transitions the debrief to "completed" and, when the flight is already approved, re-runs the idempotent flight-effects engine so any pending finance/training continuity is reconciled.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/app.flightDebriefUpsertDTO'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.flightDebriefView'
      security:
        - BearerAuth: []
      summary: Mark the debrief as completed
      tags:
        - operations
  /orgs/{orgId}/ops/flights/{flightId}/dispatch:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/app.dispatchTransitionRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Dispatch a flight (transitions ready → dispatched)
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/dispatch-decisions:
    get:
      description: Returns the immutable audit trail of every gate evaluation that produced a state change attempt (allowed, overridden, denied).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchDecisionListResponse'
      security:
        - BearerAuth: []
      summary: List dispatch decisions for a flight
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/documents:
    post:
      description: Uploads a document (up to 32 MiB) and attaches it to a specific flight. Accepts multipart form data with a file field and optional title/category.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  description: Document file
                  type: string
                  contentMediaType: application/octet-stream
                title:
                  description: Document title
                  type: string
                category:
                  description: Document category
                  type: string
              required:
                - file
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Upload a flight document
      tags:
        - flights
  /orgs/{orgId}/ops/flights/{flightId}/gate:
    get:
      description: Evaluates all dispatch rules for the target ops_status without changing flight state. Returns allowed/overridable/blocked outcome with full rule breakdown.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
        - description: Target ops_status to evaluate against (planned|ready|dispatched|flying|returned|flown|cancelled|no_show)
          in: query
          name: target_ops_status
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchGateResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Preview dispatch gate evaluation
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/mark-flown:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Close flight as flown (terminal)
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/mark-flying:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Mark flight as flying (block-out)
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/mark-no-show:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Mark flight as no-show
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/mark-ready:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/app.dispatchTransitionRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Mark flight ready for dispatch
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/mark-returned:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchTransitionResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Mark flight as returned (landing recorded)
      tags:
        - dispatch
  /orgs/{orgId}/ops/flights/{flightId}/submit:
    post:
      description: Transitions a flight to the pending-validation status, signaling it is ready for instructor or admin review.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Submit flight for review
      tags:
        - flights
  /orgs/{orgId}/ops/flights/stats:
    get:
      description: Returns aggregated flight statistics for the organization, such as total hours, flight counts, and breakdowns by status.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Flight statistics
      tags:
        - flights
  /orgs/{orgId}/ops/notams:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by ICAO
          in: query
          name: icao
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List NOTAM items for the org
      tags:
        - operations
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.notamCreateDTO'
        description: NOTAM payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.notamItemDTO'
      security:
        - BearerAuth: []
      summary: Add a NOTAM item to the org-curated list
      tags:
        - operations
  /orgs/{orgId}/ops/notams/{notamId}:
    delete:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: NOTAM item ID
          in: path
          name: notamId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Remove a NOTAM item from the org-curated list
      tags:
        - operations
  /orgs/{orgId}/ops/settings/dispatch-policy:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchPolicyResponse'
      security:
        - BearerAuth: []
      summary: List org dispatch policy overrides
      tags:
        - dispatch
    put:
      description: Only the 4 configurable rules can be modified. Other rules return 404 rule_not_configurable.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.dispatchPolicyPutRequest'
        description: Policy items to upsert
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.dispatchPolicyResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Update org dispatch policy overrides
      tags:
        - dispatch
  /orgs/{orgId}/ops/twin:
    get:
      description: Builds and returns the operations digital twin model for the organization over a date range, including resources, events, blocks, conflicts, and KPIs.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start date (RFC3339 or YYYY-MM-DD)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339 or YYYY-MM-DD)
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get operations digital twin
      tags:
        - operations
  /orgs/{orgId}/ops/twin/simulate:
    post:
      description: Applies hypothetical scheduling changes to the ops twin model and returns the simulated result with conflict and utilization deltas.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Simulate operations twin changes
      tags:
        - operations
  /orgs/{orgId}/ops/twin/views/{view}:
    get:
      description: Returns a pre-scoped ops twin snapshot for today, tomorrow, this week, or the last 14 days, along with headline counts, hotspots, and action items tailored to the view.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: View key (today|tomorrow|week|history)
          in: path
          name: view
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get operations twin for a named view
      tags:
        - operations
  /orgs/{orgId}/partner_apps:
    get:
      description: Returns all partner apps accessible to the organization, including global partners.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List partner apps
      tags:
        - integrations
    post:
      description: Creates a new partner app registration for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a partner app
      tags:
        - integrations
  /orgs/{orgId}/partner_apps/{appId}/api_keys:
    get:
      description: Returns all API keys associated with a partner app.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Partner App ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List API keys for a partner app
      tags:
        - integrations
    post:
      description: Generates a new API key with specified scopes for the partner app. The raw key is returned only once.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Partner App ID
          in: path
          name: appId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an API key for a partner app
      tags:
        - integrations
  /orgs/{orgId}/partner_apps/{appId}/api_keys/{keyId}:
    delete:
      description: Revokes an API key for a partner app, preventing further use.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Partner App ID
          in: path
          name: appId
          required: true
          schema:
            type: string
        - description: API Key ID
          in: path
          name: keyId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke an API key
      tags:
        - integrations
  /orgs/{orgId}/payments:
    get:
      description: Returns a paginated list of payments for the organization. Supports filtering by student, date range, and status.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List payments
      tags:
        - finance
    post:
      description: Records a new payment against a student or enrollment. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.PaymentCreateDTO'
        description: Payment payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Create payment
      tags:
        - finance
  /orgs/{orgId}/payments/{paymentId}:
    delete:
      description: Soft-deletes a payment record. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete payment
      tags:
        - finance
    get:
      description: Returns a single payment record by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get payment
      tags:
        - finance
    patch:
      description: Partially updates an existing payment record. Only supplied fields are changed. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.paymentPatchDTO'
        description: Payment patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update payment
      tags:
        - finance
  /orgs/{orgId}/payments/{paymentId}/allocate:
    delete:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Clear the invoice allocation on a manual payment
      tags:
        - finance
    post:
      description: Attaches a non-Stripe payment to an invoice so it appears
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.paymentAllocationDTO'
        description: Allocation payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Allocate a manual payment to an invoice
      tags:
        - finance
  /orgs/{orgId}/payments/{paymentId}/receipt.pdf:
    get:
      description: Returns a PDF receipt for any payment registered against
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: PDF receipt document
          content:
            application/pdf:
              schema:
                type: string
        '401':
          description: UNAUTHORIZED
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: PDF_SERVICE_UNAVAILABLE
          content:
            application/pdf:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Download payment receipt PDF (operator)
      tags:
        - finance
  /orgs/{orgId}/payments/{paymentId}/status:
    patch:
      description: Updates only the status field of an existing payment. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.paymentPatchDTO'
        description: Payment status payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update payment status
      tags:
        - finance
  /orgs/{orgId}/planning/blocks:
    get:
      description: Returns availability blocks for the organization within an optional date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Range start (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: Range end (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Resource ID filter
          in: query
          name: resource_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List planning blocks
      tags:
        - planning
    post:
      description: Creates an availability or unavailability block for a planning resource.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create planning block
      tags:
        - planning
  /orgs/{orgId}/planning/blocks/{blockId}:
    delete:
      description: Deletes a planning block.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Block ID
          in: path
          name: blockId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete planning block
      tags:
        - planning
    patch:
      description: Partially updates an existing planning block.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Block ID
          in: path
          name: blockId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update planning block
      tags:
        - planning
  /orgs/{orgId}/planning/conflicts/check:
    post:
      description: Checks for scheduling conflicts between planning events in a given time range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Check planning conflicts
      tags:
        - planning
  /orgs/{orgId}/planning/events:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Range start (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: Range end (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Resource type filter
          in: query
          name: resource_type
          schema:
            type: string
        - description: Resource ID filter
          in: query
          name: resource_id
          schema:
            type: string
        - description: Include expanded recurrence instances
          in: query
          name: include_instances
          schema:
            type: boolean
        - description: Include resolved link display names
          in: query
          name: include_link_display
          schema:
            type: boolean
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
        - description: Limit
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: Sort field
          in: query
          name: sort
          schema:
            type: string
        - description: Sort order
          in: query
          name: order
          schema:
            type: string
        - description: Pagination cursor
          in: query
          name: cursor
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPlanningEventsListResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List planning events
      tags:
        - planning
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.planningEventCreateRequest'
        description: Planning event payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create planning event
      tags:
        - planning
  /orgs/{orgId}/planning/events/{eventId}:
    delete:
      description: Deletes a planning event. For recurring events, supports single-occurrence and following-occurrence delete scopes.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete planning event
      tags:
        - planning
    patch:
      description: Partially updates an existing planning event. Supports single-occurrence and following-occurrence edit scopes for recurring events.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.planningEventPatchRequest'
        description: Planning event patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update planning event
      tags:
        - planning
  /orgs/{orgId}/planning/events/{eventId}/create-flight:
    post:
      description: Materialises a planning event (lesson, sim, etc.) into an ops
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Planning event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Flight already exists for this event
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '201':
          description: Flight created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an ops flight from a planning event
      tags:
        - planning
  /orgs/{orgId}/planning/events/{eventId}/links:
    get:
      description: Returns the resource links associated with a planning event.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get planning event links
      tags:
        - planning
    put:
      description: Replaces all resource links on a planning event with the provided set.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Replace planning event links
      tags:
        - planning
  /orgs/{orgId}/planning/events/{eventId}/recurrence:
    delete:
      description: Removes the recurrence rule and all future instances for a planning event.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete planning event recurrence
      tags:
        - planning
    post:
      description: Creates or replaces the recurrence rule for a planning event, expanding future instances.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Event ID
          in: path
          name: eventId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Upsert planning event recurrence
      tags:
        - planning
  /orgs/{orgId}/planning/events/{id}/apply-penalty:
    post:
      description: 'V1: manual trigger only (auto_charge_enabled is FALSE by'
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Planning Event ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Apply the computed penalty as an idempotent charge
      tags:
        - planning
  /orgs/{orgId}/planning/events/{id}/cancel-preview:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Planning Event ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Compute the penalty that would apply if this event were
      tags:
        - planning
  /orgs/{orgId}/planning/events/{id}/mark-no-show:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Planning event ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Mark a planning event as no-show
      tags:
        - planning
  /orgs/{orgId}/planning/no-show-policy:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Get the active no-show / cancellation policy for the org
      tags:
        - planning
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.noShowPolicyPatchRequest'
        description: Policy payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Upsert the active no-show / cancellation policy for the org
      tags:
        - planning
  /orgs/{orgId}/planning/resources:
    get:
      description: Returns all planning resources (aircraft, instructors, rooms, etc.) for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Resource type filter
          in: query
          name: type
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List planning resources
      tags:
        - planning
    post:
      description: Creates a new planning resource for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.planningResourceCreateRequest'
        description: Planning resource payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create planning resource
      tags:
        - planning
  /orgs/{orgId}/planning/resources/{resourceId}:
    delete:
      description: Deletes a planning resource from the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Resource ID
          in: path
          name: resourceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete planning resource
      tags:
        - planning
    patch:
      description: Partially updates a planning resource.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Resource ID
          in: path
          name: resourceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update planning resource
      tags:
        - planning
  /orgs/{orgId}/programs:
    get:
      description: Returns all training programs for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training programs
      tags:
        - students
    post:
      description: Creates a new training program for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create training program
      tags:
        - students
  /orgs/{orgId}/programs/{programId}:
    delete:
      description: Deletes a training program. Fails if the program is in use by enrollments.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete training program
      tags:
        - students
    get:
      description: Returns a single training program by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get training program
      tags:
        - students
    patch:
      description: Partially updates a training program.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update training program
      tags:
        - students
  /orgs/{orgId}/programs/{programId}/items:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List the syllabus items (snapshot) of an org-owned training program.
      tags:
        - students
  /orgs/{orgId}/reports/audit_bundle.zip:
    get:
      description: Generates a ZIP archive containing all report CSVs and organization metadata for auditing.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start timestamp
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            application/zip:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/zip:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export audit bundle as ZIP
      tags:
        - reports
  /orgs/{orgId}/reports/audit_log.csv:
    get:
      description: Exports the audit log as a CSV file for the given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start timestamp
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export audit log as CSV
      tags:
        - reports
  /orgs/{orgId}/reports/change_log.csv:
    get:
      description: Exports the change log as a CSV file for the given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start timestamp
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export change log as CSV
      tags:
        - reports
  /orgs/{orgId}/reports/compliance:
    get:
      description: Renders the compliance report as an HTML page.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Force download (1/true/yes)
          in: query
          name: download
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/html:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Render compliance report as HTML
      tags:
        - reports
  /orgs/{orgId}/reports/compliance.csv:
    get:
      description: Exports compliance items and records as a CSV file.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export compliance report as CSV
      tags:
        - reports
  /orgs/{orgId}/reports/finance:
    get:
      description: Renders the finance report as an HTML page for the given month range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start month (YYYYMM)
          in: query
          name: from
          schema:
            type: string
        - description: End month (YYYYMM)
          in: query
          name: to
          schema:
            type: string
        - description: Force download (1/true/yes)
          in: query
          name: download
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/html:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Render finance report as HTML
      tags:
        - reports
  /orgs/{orgId}/reports/finance.csv:
    get:
      description: Exports monthly finance data as a CSV file for the given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start month (YYYYMM)
          in: query
          name: from
          schema:
            type: string
        - description: End month (YYYYMM)
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export finance report as CSV
      tags:
        - reports
  /orgs/{orgId}/reports/maintenance:
    get:
      description: Renders the maintenance report as an HTML page.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Force download (1/true/yes)
          in: query
          name: download
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/html:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Render maintenance report as HTML
      tags:
        - reports
  /orgs/{orgId}/reports/maintenance.csv:
    get:
      description: Exports maintenance due items and work orders as a CSV file.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export maintenance report as CSV
      tags:
        - reports
  /orgs/{orgId}/reports/ops:
    get:
      description: Renders the operations report as an HTML page for the given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start timestamp
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp
          in: query
          name: to
          schema:
            type: string
        - description: Force download (1/true/yes)
          in: query
          name: download
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/html:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Render ops report as HTML
      tags:
        - reports
  /orgs/{orgId}/reports/ops_flights.csv:
    get:
      description: Exports flight operations data as a CSV file for the given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Start timestamp
          in: query
          name: from
          schema:
            type: string
        - description: End timestamp
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export ops flights report as CSV
      tags:
        - reports
  /orgs/{orgId}/reports/training:
    get:
      description: Renders the training report as an HTML page.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Force download (1/true/yes)
          in: query
          name: download
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/html:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Render training report as HTML
      tags:
        - reports
  /orgs/{orgId}/reports/training.csv:
    get:
      description: Exports training program data as a CSV file.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export training report as CSV
      tags:
        - reports
  /orgs/{orgId}/risk-signals:
    get:
      description: Returns risk signals for the organization with optional severity, type, and date filters. Supports both limit/offset and page/page_size pagination.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by severity (low, medium, high, warn, warning, critical)
          in: query
          name: severity
          schema:
            type: string
        - description: Filter by signal type
          in: query
          name: type
          schema:
            type: string
        - description: Filter by signal date (YYYY-MM-DD)
          in: query
          name: as_of_date
          schema:
            type: string
        - description: Max results (default 100, max 500)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset for pagination
          in: query
          name: offset
          schema:
            type: integer
        - description: Page number (alternative to limit/offset)
          in: query
          name: page
          schema:
            type: integer
        - description: Page size (alternative to limit/offset)
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List risk signals
      tags:
        - risk
  /orgs/{orgId}/security/audit-logs:
    get:
      description: Returns paginated security audit log entries for the organization, with optional filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
        - description: Filter by action
          in: query
          name: action
          schema:
            type: string
        - description: Filter by entity type
          in: query
          name: entity_type
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List security audit logs
      tags:
        - security
  /orgs/{orgId}/security/idp:
    get:
      description: Returns all identity provider configurations for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List IDP configurations
      tags:
        - security
    post:
      description: Creates a new identity provider configuration (SAML or OIDC) for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.idpConfigCreateRequest'
        description: IDP config payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create IDP configuration
      tags:
        - security
  /orgs/{orgId}/security/idp/{id}:
    delete:
      description: Permanently removes an identity provider configuration from the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: IDP Config ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete IDP configuration
      tags:
        - security
    get:
      description: Returns a single identity provider configuration.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: IDP Config ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get IDP configuration by ID
      tags:
        - security
    patch:
      description: Partially updates an existing identity provider configuration.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: IDP Config ID
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.idpConfigPatchRequest'
        description: IDP config patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update IDP configuration
      tags:
        - security
  /orgs/{orgId}/security/idp/{id}/test:
    post:
      description: Runs a connectivity test against a saved identity provider configuration.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: IDP Config ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Test IDP configuration by ID
      tags:
        - security
  /orgs/{orgId}/security/idp/test:
    post:
      description: Runs a connectivity test against an IDP configuration provided in the request body (without saving).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.idpConfigCreateRequest'
        description: IDP config to test
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Test IDP configuration from request body
      tags:
        - security
  /orgs/{orgId}/security/memberships/{id}/roles:
    post:
      description: Assigns one or more roles to a specific organization membership.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Membership ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Assign roles to membership
      tags:
        - security
  /orgs/{orgId}/security/mfa/coverage:
    get:
      description: Returns per-role enrollment counts so the Security settings page can
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: MFA enrollment coverage for an organization
      tags:
        - security
  /orgs/{orgId}/security/policies:
    get:
      description: Returns the organization's security policy settings (password rules, MFA, session limits, etc.).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get security policies
      tags:
        - security
    patch:
      description: Partially updates the organization's security policy settings.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update security policies
      tags:
        - security
  /orgs/{orgId}/security/rate-limits:
    get:
      description: Returns all configured rate limit rules for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List rate limits
      tags:
        - security
    post:
      description: Creates a new rate limit rule or updates an existing one.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create or update rate limit
      tags:
        - security
  /orgs/{orgId}/security/rate-limits/{id}:
    delete:
      description: Permanently removes a rate limit rule.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Limit ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete rate limit
      tags:
        - security
    get:
      description: Returns a single rate limit rule.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Limit ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get rate limit by ID
      tags:
        - security
    patch:
      description: Partially updates an existing rate limit rule.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Limit ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update rate limit
      tags:
        - security
  /orgs/{orgId}/security/roles:
    get:
      description: Returns all custom and system roles for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List roles
      tags:
        - security
    post:
      description: Creates a new custom role with the specified permissions.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.roleCreateRequest'
        description: Role creation payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create role
      tags:
        - security
  /orgs/{orgId}/security/roles/{id}:
    delete:
      description: Permanently removes a custom role from the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Role ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete role
      tags:
        - security
    get:
      description: Returns a single role with its metadata.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Role ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get role by ID
      tags:
        - security
    patch:
      description: Partially updates an existing role's name, description, or permissions.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Role ID
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.rolePatchRequest'
        description: Role patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update role
      tags:
        - security
  /orgs/{orgId}/security/roles/{id}/permissions:
    get:
      description: Returns the list of permissions assigned to a specific role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Role ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get role permissions
      tags:
        - security
    post:
      description: Replaces the full set of permissions for a role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Role ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Set role permissions
      tags:
        - security
  /orgs/{orgId}/security/roles/assign:
    post:
      description: Assigns roles to a membership using membership ID provided in the request body.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Assign roles to membership by body
      tags:
        - security
  /orgs/{orgId}/security/roles/permissions:
    get:
      description: Returns the full catalog of assignable permissions for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List available permissions
      tags:
        - security
  /orgs/{orgId}/security/schedulers/health:
    get:
      description: Returns health status for all org-scoped schedulers including leader election and run statistics.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get scheduler health overview
      tags:
        - internal
  /orgs/{orgId}/security/schedulers/health/{name}:
    get:
      description: Returns health status for a specific named scheduler within the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Scheduler name
          in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get scheduler health by name
      tags:
        - internal
  /orgs/{orgId}/security/scim/tokens:
    get:
      description: Returns all SCIM provisioning tokens for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List SCIM tokens
      tags:
        - security
    post:
      description: Creates a new SCIM provisioning token with optional scope restrictions and expiry.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.scimTokenCreateRequest'
        description: SCIM token creation payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create SCIM token
      tags:
        - security
  /orgs/{orgId}/security/scim/tokens/{id}:
    delete:
      description: Revokes an existing SCIM provisioning token by ID.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: SCIM Token ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke SCIM token
      tags:
        - security
  /orgs/{orgId}/settings/invites:
    get:
      description: Returns all pending invites for the organization. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List invites
      tags:
        - settings
  /orgs/{orgId}/settings/leave:
    post:
      description: Removes the current user's membership from the organization. Cannot be used by the sole owner.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.leaveOrganizationRequest'
        description: Leave reason payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Leave organization
      tags:
        - settings
  /orgs/{orgId}/settings/members:
    post:
      description: Invites a new member to the organization by email, or creates a direct membership if the user already exists. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.membershipCreateRequest'
        description: Invite or membership payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create invite or membership
      tags:
        - settings
  /orgs/{orgId}/settings/members/{membershipId}:
    delete:
      description: Removes a member from the organization. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Membership ID
          in: path
          name: membershipId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiDeleteEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Remove membership
      tags:
        - settings
  /orgs/{orgId}/settings/members/{membershipId}/role:
    patch:
      description: Changes the role of an existing organization member. Requires org admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Membership ID
          in: path
          name: membershipId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.membershipPatchRequest'
        description: Role update payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update membership role
      tags:
        - settings
  /orgs/{orgId}/setup-checklist:
    get:
      description: Returns the 13-item operational setup checklist for an organization,
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.setupChecklistResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: SERVER_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get setup checklist (V1 customer-usable program)
      tags:
        - setup
  /orgs/{orgId}/students:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Search query
          in: query
          name: q
          schema:
            type: string
        - description: Student status
          in: query
          name: status
          schema:
            type: string
        - description: Program ID
          in: query
          name: program
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
        - description: Limit
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: Sort field
          in: query
          name: sort
          schema:
            type: string
        - description: Sort order
          in: query
          name: order
          schema:
            type: string
        - description: Pagination cursor
          in: query
          name: cursor
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List students
      tags:
        - students
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.studentUpsertRequest'
        description: Student payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create student
      tags:
        - students
  /orgs/{orgId}/students/{studentId}:
    delete:
      description: Soft-deletes a student. Fails if the student has active enrollments.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete student
      tags:
        - students
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student
      tags:
        - students
    patch:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.studentUpsertRequest'
        description: Student patch payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update student
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/dossier/documents:
    get:
      description: Returns a paginated list of all documents (student documents and flight documents) in a student's dossier for a given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student dossier documents
      tags:
        - flights
  /orgs/{orgId}/students/{studentId}/dossier/flights:
    get:
      description: Returns a paginated list of flights in a student's dossier, filterable by status and date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Filter by flight status
          in: query
          name: status
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student dossier flights
      tags:
        - flights
  /orgs/{orgId}/students/{studentId}/dossier/flights.csv:
    get:
      description: Exports a student's dossier flights as a downloadable CSV file, filterable by status and date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Filter by flight status
          in: query
          name: status
          schema:
            type: string
      responses:
        '200':
          description: CSV file
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export student dossier flights as CSV
      tags:
        - flights
  /orgs/{orgId}/students/{studentId}/dossier/ledger:
    get:
      description: Returns a paginated financial ledger (payments, charges, ledger entries) for a student's dossier within a given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: to
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student dossier financial ledger
      tags:
        - flights
  /orgs/{orgId}/students/{studentId}/dossier/summary:
    get:
      description: Returns a high-level summary of a student's dossier including flight counts, document counts, ledger entries, and training metrics for a given date range.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Start date (RFC3339)
          in: query
          name: from
          schema:
            type: string
        - description: End date (RFC3339)
          in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student dossier summary
      tags:
        - flights
  /orgs/{orgId}/students/{studentId}/dossier/training:
    get:
      description: Returns training metrics, sign-off progress, and recent sign-offs for a student's dossier.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student dossier training metrics
      tags:
        - flights
  /orgs/{orgId}/students/{studentId}/instructor:
    patch:
      description: Assigns or changes the primary instructor for a student.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Assign instructor to student
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/invite:
    post:
      description: Generates and sends an email invitation for a student to claim their account. Revokes any existing pending invite for the same student. Requires instructor or admin role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT — student already linked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create student invite
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/ledger:
    get:
      description: Returns paginated financial ledger entries (charges, payments) for a student.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Start date filter (YYYY-MM-DD)
          in: query
          name: date_from
          schema:
            type: string
        - description: End date filter (YYYY-MM-DD)
          in: query
          name: date_to
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student ledger entries
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/ledger/summary:
    get:
      description: Returns a financial summary for the student including planned, paid, and outstanding amounts.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student ledger summary
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/licenses:
    get:
      description: Returns license/rating progress for a student based on compliance items and flight hours.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student licenses
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/logbook:
    get:
      description: Returns a paginated list of training flight entries for a specific student, including hour totals.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Page number (1-based)
          in: query
          name: page
          schema:
            type: integer
        - description: Items per page (max 200)
          in: query
          name: page_size
          schema:
            type: integer
        - description: Filter by flight type
          in: query
          name: flight_type
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student logbook entries
      tags:
        - logbook
    post:
      description: Creates a new training flight logbook entry for a student. Validates durations and flight type.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.logbookCreateRequest'
        description: Logbook entry details
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a logbook entry
      tags:
        - logbook
  /orgs/{orgId}/students/{studentId}/logbook.csv:
    get:
      description: Exports all training flight entries for a student as a downloadable CSV file.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: CSV file
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '401':
          description: UNAUTHORIZED
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            text/csv:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export student logbook as CSV
      tags:
        - logbook
  /orgs/{orgId}/students/{studentId}/logbook/{flightId}:
    delete:
      description: Deletes a training flight logbook entry. Only allowed when the entry has not been signed by an instructor. Requires admin or owner role.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Training flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT - entry already signed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Delete a logbook entry
      tags:
        - logbook
    get:
      description: Returns the full detail of a specific training flight logbook entry.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Training flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get a single logbook entry
      tags:
        - logbook
    patch:
      description: Partially updates a training flight logbook entry. Only allowed when the entry has not been signed by an instructor.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Training flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.logbookCreateRequest'
        description: Fields to update
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT - entry already signed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update a logbook entry
      tags:
        - logbook
  /orgs/{orgId}/students/{studentId}/logbook/{flightId}/sign:
    post:
      description: Signs a training flight logbook entry as the instructor. Only the assigned instructor or an admin may sign.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Training flight ID
          in: path
          name: flightId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT - already signed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Sign a logbook entry
      tags:
        - logbook
  /orgs/{orgId}/students/{studentId}/logbook/milestones:
    get:
      description: Returns the list of training milestones achieved by a specific student.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student training milestones
      tags:
        - logbook
  /orgs/{orgId}/students/{studentId}/logbook/totals:
    get:
      description: Returns cumulated flight hours and EASA PPL(A) progress breakdown for a specific student.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student hour totals
      tags:
        - logbook
  /orgs/{orgId}/students/{studentId}/payments/{paymentId}/link:
    post:
      description: Associates an existing payment with a student record.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Payment ID
          in: path
          name: paymentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Link payment to student
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/progress:
    get:
      description: Returns a summary of the student's training progress including hours, milestones, and next items.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student training progress
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/signoffs:
    get:
      description: Returns all signoff items for a student, ordered by pending first.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List student signoffs
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/signoffs/{signoffId}:
    get:
      description: Returns a single signoff item for a student.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Signoff ID
          in: path
          name: signoffId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get student signoff
      tags:
        - students
  /orgs/{orgId}/students/{studentId}/signoffs/{signoffId}/sign:
    post:
      description: Signs or updates a signoff item for a student, recording the instructor's approval.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
        - description: Signoff ID
          in: path
          name: signoffId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Sign student signoff
      tags:
        - students
  /orgs/{orgId}/sync/changes:
    get:
      description: Returns paginated change log entries with optional filters for version, date, entity type, and actor.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Return changes after this version
          in: query
          name: since_version
          schema:
            type: integer
        - description: Filter from date (RFC3339)
          in: query
          name: date_from
          schema:
            type: string
        - description: Filter to date (RFC3339)
          in: query
          name: date_to
          schema:
            type: string
        - description: Filter by entity type
          in: query
          name: entity_type
          schema:
            type: string
        - description: Filter by operation
          in: query
          name: action
          schema:
            type: string
        - description: Filter by actor ID
          in: query
          name: actor_id
          schema:
            type: string
        - description: Filter by device ID
          in: query
          name: device_id
          schema:
            type: string
        - description: Search query
          in: query
          name: q
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size (max 500)
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List sync changes
      tags:
        - sync
  /orgs/{orgId}/sync/changes/export:
    get:
      description: Exports change log entries as CSV or JSON file download, with optional filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: 'Export format: csv or json (default csv)'
          in: query
          name: format
          schema:
            type: string
        - description: Filter from date (RFC3339)
          in: query
          name: date_from
          schema:
            type: string
        - description: Filter to date (RFC3339)
          in: query
          name: date_to
          schema:
            type: string
        - description: Filter by entity type
          in: query
          name: entity_type
          schema:
            type: string
        - description: Filter by operation
          in: query
          name: action
          schema:
            type: string
        - description: Filter by actor ID
          in: query
          name: actor_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: string
                format: binary
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Export sync changes
      tags:
        - sync
  /orgs/{orgId}/sync/devices:
    get:
      description: Returns all sync devices for the organization, optionally filtered by status.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by status (active, archived, all)
          in: query
          name: status
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List sync devices
      tags:
        - sync
  /orgs/{orgId}/sync/devices/{deviceId}:
    get:
      description: Returns detailed information about a specific sync device including activity summary and usage patterns.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Device ID
          in: path
          name: deviceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get sync device details
      tags:
        - sync
    patch:
      description: Updates the display name of a sync device.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Device ID
          in: path
          name: deviceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.syncDeviceRow'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Rename sync device
      tags:
        - sync
  /orgs/{orgId}/sync/devices/{deviceId}/archive:
    post:
      description: Marks a sync device as archived, preventing further sync operations from it.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Device ID
          in: path
          name: deviceId
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Archive sync device
      tags:
        - sync
  /orgs/{orgId}/sync/entities/{entityType}/{entityId}/history:
    get:
      description: Returns the full change history for a specific entity, ordered by version ascending.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Entity type
          in: path
          name: entityType
          required: true
          schema:
            type: string
        - description: Entity ID
          in: path
          name: entityId
          required: true
          schema:
            type: string
        - description: Page number
          in: query
          name: page
          schema:
            type: integer
        - description: Page size (max 200)
          in: query
          name: page_size
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiPaginatedEnvelope'
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get entity change history
      tags:
        - sync
  /orgs/{orgId}/sync/health:
    get:
      description: Returns sync health metrics including volume per day, entity type breakdown, busiest hours, and device stats.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter from date (RFC3339)
          in: query
          name: date_from
          schema:
            type: string
        - description: Filter to date (RFC3339)
          in: query
          name: date_to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get sync health metrics
      tags:
        - sync
  /orgs/{orgId}/sync/overview:
    get:
      description: Returns a high-level overview of sync activity including total changes, entity breakdown, and active devices.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter from date (RFC3339)
          in: query
          name: date_from
          schema:
            type: string
        - description: Filter to date (RFC3339)
          in: query
          name: date_to
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get sync overview
      tags:
        - sync
  /orgs/{orgId}/sync/push:
    post:
      description: Accepts a batch of offline changes and applies them to the change log within a transaction.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.SyncPushRequest'
        description: Batch of changes to push
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Push offline changes
      tags:
        - sync
  /orgs/{orgId}/training/dashboard:
    get:
      description: Returns global stats, condensed student alert list, and instructor workloads computed in parallel.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get training dashboard overview
      tags:
        - training
  /orgs/{orgId}/training/dashboard/alerts:
    get:
      description: Returns training alerts for the organization, with optional instructor and severity filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by instructor ID
          in: query
          name: instructor_id
          schema:
            type: string
        - description: Filter by severity (critical, warning, info)
          in: query
          name: severity
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training dashboard alerts
      tags:
        - training
  /orgs/{orgId}/training/dashboard/instructors:
    get:
      description: Returns paginated instructor workload data for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training dashboard instructors
      tags:
        - training
  /orgs/{orgId}/training/dashboard/students:
    get:
      description: Returns paginated student progress data with optional instructor, program, and alerts-only filters.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by instructor ID
          in: query
          name: instructor_id
          schema:
            type: string
        - description: Filter by program ID
          in: query
          name: program_id
          schema:
            type: string
        - description: Only return students with alerts
          in: query
          name: alerts_only
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training dashboard students
      tags:
        - training
  /orgs/{orgId}/training/instructors:
    get:
      description: Returns all organization members with instructor, admin, or owner roles.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training instructors
      tags:
        - students
  /orgs/{orgId}/training/invoices:
    get:
      description: Returns training invoices for the organization, optionally filtered by student or status.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by student ID
          in: query
          name: student_id
          schema:
            type: string
        - description: Filter by invoice status
          in: query
          name: status
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training invoices
      tags:
        - billing
  /orgs/{orgId}/training/invoices/{invoiceId}:
    get:
      description: Returns a single training invoice with its line items.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get training invoice
      tags:
        - billing
  /orgs/{orgId}/training/invoices/{invoiceId}/dunning:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get an invoice's dunning state + next action
      tags:
        - finance
  /orgs/{orgId}/training/invoices/{invoiceId}/dunning/pause:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.pauseDunningDTO'
        description: Pause payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Pause dunning follow-up for an invoice
      tags:
        - finance
  /orgs/{orgId}/training/invoices/{invoiceId}/dunning/resume:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: Resume dunning follow-up on a paused invoice
      tags:
        - finance
  /orgs/{orgId}/training/invoices/{invoiceId}/items:
    post:
      description: Adds a manual line item to a draft training invoice.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Add manual invoice item
      tags:
        - billing
  /orgs/{orgId}/training/invoices/{invoiceId}/payments:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List payments linked to an invoice
      tags:
        - finance
  /orgs/{orgId}/training/invoices/{invoiceId}/pdf:
    get:
      description: Returns a printable HTML representation of the training invoice.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: HTML invoice document
          content:
            text/html:
              schema:
                type: string
        '401':
          description: UNAUTHORIZED
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Download invoice as HTML/PDF
      tags:
        - billing
  /orgs/{orgId}/training/invoices/{invoiceId}/receivables:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get one invoice's receivables truth
      tags:
        - finance
  /orgs/{orgId}/training/invoices/{invoiceId}/reminders:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List reminder events for an invoice
      tags:
        - finance
    post:
      description: 'Increments the invoice''s dunning level and appends one invoice_reminders row. Honesty rule: channel=email only writes status=sent if the provider confirmed delivery; the dev email provider writes channel=email_dev_log + status=recorded instead, so "sent" is never faked.'
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.recordReminderDTO'
        description: Reminder payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Record a dunning reminder (send email, log manual, or skip)
      tags:
        - finance
  /orgs/{orgId}/training/invoices/{invoiceId}/status:
    patch:
      description: Transitions a training invoice to a new status (e.g. sent, paid, void).
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Invoice ID
          in: path
          name: invoiceId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update training invoice status
      tags:
        - billing
  /orgs/{orgId}/training/invoices/generate:
    post:
      description: Generates a new training invoice from unbilled flights for a student.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Generate training invoice
      tags:
        - billing
  /orgs/{orgId}/training/invoices/preview:
    post:
      description: Previews what a training invoice would look like without persisting it.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: UNPROCESSABLE_ENTITY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Preview training invoice
      tags:
        - billing
  /orgs/{orgId}/training/licenses:
    get:
      description: Returns the catalog of available licenses and ratings for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List license catalog
      tags:
        - students
  /orgs/{orgId}/training/programs/{programId}/at-risk:
    get:
      description: |-
        Returns students flagged by explicit rules:
        no_recent_activity, low_signoff_coverage,
        missing_critical_signoffs, template_version_lag.
        No opaque risk scores — every reason is a boolean rule.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program UUID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/training.ProgramAtRiskList'
      security:
        - BearerAuth: []
      summary: At-risk students for a program
      tags:
        - training
  /orgs/{orgId}/training/programs/{programId}/heatmap:
    get:
      description: |-
        Returns a matrix of every active enrolled student and
        every program item, where each cell is one of
        "signed", "pending", "unsigned", or "not_required".
        Includes top_blocking_items for quick triage.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program UUID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/training.ProgramHeatmap'
      security:
        - BearerAuth: []
      summary: Program progress heatmap (student × item matrix)
      tags:
        - training
  /orgs/{orgId}/training/programs/{programId}/migration/apply:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Apply a template-version migration to a program
      tags:
        - training
  /orgs/{orgId}/training/programs/{programId}/migration/events:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      security:
        - BearerAuth: []
      summary: List migration audit events for a program
      tags:
        - training
  /orgs/{orgId}/training/programs/{programId}/migration/preview:
    post:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program ID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Preview a template-version migration for a program
      tags:
        - training
  /orgs/{orgId}/training/programs/{programId}/progress:
    get:
      description: |-
        Returns aggregate progress stats for a program: avg
        coverage %, student count, on-track / behind / at-risk
        counts, and template version lag. Signoff-based — hours
        are covered by the existing /training/dashboard endpoint.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Program UUID
          in: path
          name: programId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/training.ProgramProgressSummary'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Program progress KPI summary
      tags:
        - training
  /orgs/{orgId}/training/rate-cards:
    get:
      description: Returns all training rate cards for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List rate cards
      tags:
        - billing
    post:
      description: Creates a new training rate card for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create rate card
      tags:
        - billing
  /orgs/{orgId}/training/rate-cards/{rateCardId}:
    get:
      description: Returns a single rate card with its line items.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Card ID
          in: path
          name: rateCardId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get rate card
      tags:
        - billing
    patch:
      description: Partially updates a training rate card.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Card ID
          in: path
          name: rateCardId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update rate card
      tags:
        - billing
  /orgs/{orgId}/training/rate-cards/{rateCardId}/activate:
    post:
      description: Sets a rate card as the active card for the organization, deactivating any previously active one.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Card ID
          in: path
          name: rateCardId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Activate rate card
      tags:
        - billing
  /orgs/{orgId}/training/rate-cards/{rateCardId}/items:
    put:
      description: Replaces all line items on a rate card with the provided set.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Rate Card ID
          in: path
          name: rateCardId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Replace rate card items
      tags:
        - billing
  /orgs/{orgId}/training/students/{studentId}/unbilled-flights:
    get:
      description: Returns flights for a student that have not yet been billed on any training invoice.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Student ID
          in: path
          name: studentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '500':
          description: INTERNAL_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get unbilled flights
      tags:
        - billing
  /orgs/{orgId}/training/templates:
    get:
      description: Returns the published EASA / DTO / ORA training templates available to all organizations.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Filter by rating (PPL_A, LAPL_A, CPL_A, IR_SE, IR_ME, FI_A, CRI_A)
          in: query
          name: rating
          schema:
            type: string
        - description: Filter by applicability (dto, ora)
          in: query
          name: applicable
          schema:
            type: string
        - description: 'Filter by status (default: published)'
          in: query
          name: status
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List training templates (global catalog)
      tags:
        - training
    post:
      description: |-
        Creates a new program template scoped to the calling org. The
        final slug is `org-<8-hex-of-orgId>-<slug_suffix>`. Optionally
        forks the items from the current published version of an
        existing template via fork_from_slug.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create an org-authored program template
      tags:
        - training
  /orgs/{orgId}/training/templates/{slug}:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug (e.g. easa-ppl-a-v1)
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get training template detail (with syllabus tree for the current published version)
      tags:
        - training
  /orgs/{orgId}/training/templates/{slug}/apply:
    post:
      description: |-
        Clones the template's current published version into a new org-owned training
        program and program_items snapshot. Optionally switches an existing enrollment
        to the new program.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Apply a training template to the organization (creates a program snapshot)
      tags:
        - training
  /orgs/{orgId}/training/templates/{slug}/diff:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug
          in: path
          name: slug
          required: true
          schema:
            type: string
        - description: From version
          in: query
          name: from
          required: true
          schema:
            type: integer
        - description: To version
          in: query
          name: to
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Compare two versions of a training template by item code
      tags:
        - training
  /orgs/{orgId}/training/templates/{slug}/versions:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List all versions of a training template
      tags:
        - training
  /orgs/{orgId}/training/templates/{slug}/versions/{version}:
    get:
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Template slug
          in: path
          name: slug
          required: true
          schema:
            type: string
        - description: Version number
          in: path
          name: version
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Fetch a specific version of a training template (with item tree)
      tags:
        - training
  /orgs/{orgId}/webhooks/deliveries:
    get:
      description: Returns recent webhook delivery attempts for the organization's endpoints.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List webhook deliveries
      tags:
        - integrations
  /orgs/{orgId}/webhooks/endpoints:
    get:
      description: Returns all webhook endpoints configured for the organization.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List webhook endpoints
      tags:
        - integrations
    post:
      description: Registers a new webhook endpoint with the specified URL and event type subscriptions. Returns the signing secret.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a webhook endpoint
      tags:
        - integrations
  /orgs/{orgId}/webhooks/endpoints/{endpointId}:
    patch:
      description: Updates the URL, event types, or enabled status of a webhook endpoint.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook Endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Update a webhook endpoint
      tags:
        - integrations
  /orgs/{orgId}/webhooks/endpoints/{endpointId}/test:
    post:
      description: Sends a test event to the specified webhook endpoint to verify connectivity.
      parameters:
        - description: Organization ID
          in: path
          name: orgId
          required: true
          schema:
            type: string
        - description: Webhook Endpoint ID
          in: path
          name: endpointId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '502':
          description: BAD_GATEWAY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Send a test webhook delivery
      tags:
        - integrations
  /orgs/current:
    get:
      description: Resolves organization from multi-tenant headers/query for global routes.
      parameters:
        - description: Organization ID for context resolution
          in: header
          name: X-Org-Id
          schema:
            type: string
        - description: Alias for organization context resolution
          in: header
          name: X-Organization-Id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: org_id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiOrganizationResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get current organization (org switch context)
      tags:
        - orgs
  /pilot_identity/by_profile/{profileId}:
    get:
      description: Same as /pilot_identity/by_user/{userId} but keyed on the pilot_profiles.id.
      parameters:
        - description: Pilot profile ID
          in: path
          name: profileId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.portableIdentityResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get portable pilot identity by pilot profile id
      tags:
        - pilot-identity
  /pilot_identity/by_user/{userId}:
    get:
      description: Returns the portable identity snapshot for any user. Org links and timeline events tied to a non-consented org are filtered out for non-owners.
      parameters:
        - description: Global user ID
          in: path
          name: userId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.portableIdentityResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get portable pilot identity by user id
      tags:
        - pilot-identity
  /pilot_identity/me:
    get:
      description: Returns the portable identity snapshot of the authenticated pilot, including alumni links, career stage, and full timeline.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.portableIdentityResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my portable pilot identity
      tags:
        - pilot-identity
  /pilot_profile/career_event:
    post:
      description: Records a new career event (e.g., training milestone, license obtained) on a pilot profile. Profile owners can create events directly; org members need consent.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.pilotProfileCareerEventRequest'
        description: Career event details
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.pilotCareerEventResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create a pilot career event
      tags:
        - pilot-profile
  /pilot_profile/career_timeline:
    get:
      description: Returns the career event timeline for a pilot profile. Profile owners see all events; org members with consent see events scoped to their organization.
      parameters:
        - description: Target pilot profile ID (defaults to own)
          in: query
          name: pilot_profile_id
          schema:
            type: string
        - description: Organization context for cross-profile access
          in: query
          name: organization_id
          schema:
            type: string
        - description: Max items to return
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset for pagination
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get pilot career timeline
      tags:
        - pilot-profile
  /pilot_profile/link_org:
    post:
      description: Requests, grants, or revokes a consent-based link between a pilot profile and an organization. Supports request/grant/revoke actions.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.pilotProfileLinkOrgRequest'
        description: Link details
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.pilotOrgLinkResponse'
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.pilotOrgLinkResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Link pilot profile to an organization
      tags:
        - pilot-profile
  /pilot_profile/me:
    get:
      description: Returns the authenticated user's pilot profile and associated organization links. Creates the profile on first access.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get my pilot profile
      tags:
        - pilot-profile
  /public/auth/account-kind:
    post:
      description: Returns whether an email belongs to a console account, a site-only account, or is unknown. Used by the marketing site to decide whether to redirect to the console for login. Always returns 200 OK with the same shape to prevent account enumeration.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.accountKindRequest'
        description: Account kind lookup payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.accountKindResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Account kind lookup
      tags:
        - auth
  /public/compliance/calendar.ics:
    get:
      description: |-
        No auth. Token is hashed in DB; expired or revoked tokens
        return 410. Returns the pilot's items across every org
        they have a `people` row in (cross-org pilot record).
      parameters:
        - description: Share token
          in: query
          name: token
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            text/calendar:
              schema:
                type: string
                format: binary
      summary: Per-pilot compliance calendar (.ics) via share token
      tags:
        - compliance
  /public/directory/aircraft:
    get:
      description: Returns a paginated list of published aircraft in the public directory. Supports search and filtering by category, country, IFR, avionics, and availability.
      parameters:
        - description: Search query (registration, type, model, city, country, ICAO)
          in: query
          name: q
          schema:
            type: string
        - description: Filter by aircraft category
          in: query
          name: category
          schema:
            type: string
        - description: Filter by country
          in: query
          name: country
          schema:
            type: string
        - description: Filter by IFR capability
          in: query
          name: ifr
          schema:
            type: boolean
        - description: Filter by rental availability
          in: query
          name: available
          schema:
            type: boolean
        - description: Filter by avionics (comma-separated)
          in: query
          name: avionics
          schema:
            type: string
        - description: Items per page
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directoryListResponse'
      summary: List directory aircraft
      tags:
        - directory
    post:
      description: Creates a new aircraft listing. If the caller is authenticated the listing is published immediately; otherwise an email verification is sent.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.directorySubmitAircraftRequest'
        description: Aircraft details
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directorySubmitResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Submit a new aircraft to the directory
      tags:
        - directory
  /public/directory/aircraft/{slug}:
    get:
      description: Returns the full detail of a published aircraft from the public directory.
      parameters:
        - description: Aircraft slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directoryAircraftItem'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get a directory aircraft by slug
      tags:
        - directory
  /public/directory/instructors:
    get:
      description: Returns a paginated list of published flight instructors in the public directory. Supports search and filtering.
      parameters:
        - description: Search query (name)
          in: query
          name: q
          schema:
            type: string
        - description: Filter by country
          in: query
          name: country
          schema:
            type: string
        - description: Filter by ratings (comma-separated)
          in: query
          name: ratings
          schema:
            type: string
        - description: Filter by languages (comma-separated)
          in: query
          name: languages
          schema:
            type: string
        - description: Filter by training-for tags (comma-separated)
          in: query
          name: training_for
          schema:
            type: string
        - description: Items per page
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directoryListResponse'
      summary: List directory instructors
      tags:
        - directory
    post:
      description: Creates a new instructor listing. If the caller is authenticated the listing is published immediately; otherwise an email verification is sent.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.directorySubmitInstructorRequest'
        description: Instructor details
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directorySubmitResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Submit a new instructor to the directory
      tags:
        - directory
  /public/directory/instructors/{slug}:
    get:
      description: Returns the full detail of a published instructor from the public directory.
      parameters:
        - description: Instructor slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directoryInstructorItem'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get a directory instructor by slug
      tags:
        - directory
  /public/directory/listings/{editToken}:
    delete:
      description: Permanently removes a directory listing (school, instructor, or aircraft) using the owner's edit token.
      parameters:
        - description: Edit token
          in: path
          name: editToken
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '404':
          description: NOT_FOUND
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Delete a directory listing by edit token
      tags:
        - directory
    get:
      description: Returns the full raw data of a directory listing (school, instructor, or aircraft) identified by its edit token.
      parameters:
        - description: Edit token
          in: path
          name: editToken
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get a directory listing by edit token
      tags:
        - directory
    patch:
      description: Partially updates a directory listing (school, instructor, or aircraft) using the owner's edit token. Protected fields cannot be overwritten.
      parameters:
        - description: Edit token
          in: path
          name: editToken
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Update a directory listing by edit token
      tags:
        - directory
  /public/directory/schools:
    get:
      description: Returns a paginated list of published flight schools in the public directory. Supports search and filtering by country, type, and ratings.
      parameters:
        - description: Search query (name, city, country, ICAO)
          in: query
          name: q
          schema:
            type: string
        - description: Filter by country
          in: query
          name: country
          schema:
            type: string
        - description: Filter by school type
          in: query
          name: type
          schema:
            type: string
        - description: Filter by ratings (comma-separated)
          in: query
          name: ratings
          schema:
            type: string
        - description: Items per page
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directoryListResponse'
      summary: List directory schools
      tags:
        - directory
    post:
      description: Creates a new flight school listing. If the caller is authenticated the listing is published immediately; otherwise an email verification is sent.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.directorySubmitSchoolRequest'
        description: School details
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directorySubmitResponse'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Submit a new school to the directory
      tags:
        - directory
  /public/directory/schools/{slug}:
    get:
      description: Returns the full detail of a published flight school from the public directory.
      parameters:
        - description: School slug
          in: path
          name: slug
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.directorySchoolItem'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get a directory school by slug
      tags:
        - directory
  /public/directory/verify-email/{token}:
    post:
      description: Verifies a directory listing's contact email using a one-time token. On success the listing status changes to published.
      parameters:
        - description: Email verification token
          in: path
          name: token
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '410':
          description: TOKEN_EXPIRED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Verify a directory listing email
      tags:
        - directory
  /public/marketplace/opportunities:
    get:
      description: Returns a paginated list of published opportunities. No authentication required.
      parameters:
        - description: Role filter
          in: query
          name: role
          schema:
            type: string
        - description: Location filter (partial match)
          in: query
          name: location
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: List published opportunities (public)
      tags:
        - marketplace
  /public/marketplace/opportunities/{id}:
    get:
      description: Returns a single published opportunity by ID. No authentication required.
      parameters:
        - description: Opportunity ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Get a published opportunity (public)
      tags:
        - marketplace
  /public/marketplace/talent:
    get:
      description: Returns talent profiles whose visibility is explicitly set to "public". Bio and contact handles are NOT exposed — callers must authenticate and use the in-product messaging flow to reach out. Rate limited.
      parameters:
        - description: Headline substring (min 2 chars)
          in: query
          name: q
          schema:
            type: string
        - description: Talent type filter (pilot|instructor|student)
          in: query
          name: type
          schema:
            type: string
        - description: Location substring
          in: query
          name: location
          schema:
            type: string
        - description: Language code (e.g. en, fr)
          in: query
          name: language
          schema:
            type: string
        - description: Availability status filter
          in: query
          name: availability
          schema:
            type: string
        - description: Max results (default 20, max 50)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
      summary: Search opt-in public talent profiles
      tags:
        - marketplace
  /public/trials/checkout-session:
    post:
      description: 'Public endpoint. Creates a Stripe Checkout Session for a 14-day free trial. No authentication required; rate-limited by IP. Allowed plans: foundation, operations. Returns a Stripe checkout URL for the frontend to redirect to.'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.trialCheckoutRequest'
        description: Trial checkout payload (plan, full_name, work_email, organization_name required)
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '409':
          description: CONFLICT — trial already used or pending
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '422':
          description: VALIDATION_FAILED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '429':
          description: RATE_LIMITED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '503':
          description: SERVICE_UNAVAILABLE — billing not configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Create trial checkout session
      tags:
        - billing
  /ready:
    get:
      description: Checks database (and Redis if configured). Returns ok, degraded, or unavailable.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.readinessResponse'
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.readinessResponse'
      summary: Readiness check
      tags:
        - health
  /scim/v2/Groups:
    get:
      description: Returns provisioned groups (roles) for the organization in SCIM 2.0 format.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: List SCIM groups
      tags:
        - scim
    post:
      description: Creates a new group (role) in the organization via SCIM 2.0 protocol.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.scimGroupRequest'
        description: SCIM group payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Create SCIM group
      tags:
        - scim
  /scim/v2/Groups/{id}:
    delete:
      description: Removes a group (role) from the organization via SCIM 2.0 protocol.
      parameters:
        - description: Group ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Delete SCIM group
      tags:
        - scim
    patch:
      description: Applies partial updates to a provisioned group via SCIM 2.0 PATCH operations.
      parameters:
        - description: Group ID
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/app.scimPatchRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Patch SCIM group
      tags:
        - scim
  /scim/v2/Users:
    get:
      description: Returns provisioned users for the organization in SCIM 2.0 format.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: List SCIM users
      tags:
        - scim
    post:
      description: Provisions a new user in the organization via SCIM 2.0 protocol.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.scimUserRequest'
        description: SCIM user payload
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '409':
          description: CONFLICT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Create SCIM user
      tags:
        - scim
  /scim/v2/Users/{id}:
    delete:
      description: Deprovisions a user from the organization via SCIM 2.0 protocol.
      parameters:
        - description: User ID
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Delete SCIM user
      tags:
        - scim
    patch:
      description: Applies partial updates to a provisioned user via SCIM 2.0 PATCH operations.
      parameters:
        - description: User ID
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        $ref: '#/components/requestBodies/app.scimPatchRequest'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      summary: Patch SCIM user
      tags:
        - scim
  /sessions:
    get:
      description: Lists organization sessions for global settings routes. Provide org context via header/query when path has no `{orgId}`.
      parameters:
        - description: Organization ID for context resolution
          in: header
          name: X-Org-Id
          schema:
            type: string
        - description: Alias for organization context resolution
          in: header
          name: X-Organization-Id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: org_id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiSessionsEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: List sessions for current organization
      tags:
        - sessions
  /sessions/{sessionId}/revoke:
    post:
      parameters:
        - description: Organization ID for context resolution
          in: header
          name: X-Org-Id
          schema:
            type: string
        - description: Alias for organization context resolution
          in: header
          name: X-Organization-Id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: org_id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: organization_id
          schema:
            type: string
        - description: Session ID
          in: path
          name: sessionId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiSessionRevokeEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke session by ID
      tags:
        - sessions
  /sessions/revoke:
    post:
      parameters:
        - description: Organization ID for context resolution
          in: header
          name: X-Org-Id
          schema:
            type: string
        - description: Alias for organization context resolution
          in: header
          name: X-Organization-Id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: org_id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: organization_id
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/app.sessionRevokeRequest'
        description: Session revoke payload
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiSessionRevokeEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke session by payload
      tags:
        - sessions
  /sessions/revoke_all:
    post:
      parameters:
        - description: Organization ID for context resolution
          in: header
          name: X-Org-Id
          schema:
            type: string
        - description: Alias for organization context resolution
          in: header
          name: X-Organization-Id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: org_id
          schema:
            type: string
        - description: Organization ID for context resolution
          in: query
          name: organization_id
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiRevokeAllEnvelope'
        '400':
          description: VALIDATION_ERROR
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiValidationErrorResponse'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Revoke all organization sessions
      tags:
        - sessions
  /talent/{talentId}:
    get:
      description: Returns a talent profile by its ID, including skills.
      parameters:
        - description: Talent Profile ID
          in: path
          name: talentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get talent profile by ID
      tags:
        - marketplace
  /talent/{talentId}/rating:
    post:
      description: Creates or updates a rating for a talent profile. One rating per reviewer per talent.
      parameters:
        - description: Talent Profile ID
          in: path
          name: talentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Rate a talent profile
      tags:
        - marketplace
  /talent/{talentId}/ratings_summary:
    get:
      description: Returns the average score, total count, and score distribution for a talent profile's ratings.
      parameters:
        - description: Talent Profile ID
          in: path
          name: talentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get talent ratings summary
      tags:
        - marketplace
  /talent/{talentId}/verifications:
    get:
      description: Returns the verification badges and manual verifications for a talent profile.
      parameters:
        - description: Talent Profile ID
          in: path
          name: talentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '404':
          description: NOT_FOUND
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Get talent verification badges
      tags:
        - marketplace
  /talent/{talentId}/verify:
    post:
      description: Creates or updates a manual verification record for a talent profile within an organization.
      parameters:
        - description: Talent Profile ID
          in: path
          name: talentId
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '400':
          description: BAD_REQUEST
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
        '403':
          description: FORBIDDEN
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Create or update a talent verification
      tags:
        - marketplace
  /talent/search:
    get:
      description: Returns a paginated list of talent profiles matching optional filters like role, location, and availability.
      parameters:
        - description: Search query
          in: query
          name: q
          schema:
            type: string
        - description: Profile type filter
          in: query
          name: type
          schema:
            type: string
        - description: Availability status filter
          in: query
          name: availability
          schema:
            type: string
        - description: Max results
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
        '401':
          description: UNAUTHORIZED
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app.openapiErrorEnvelope'
      security:
        - BearerAuth: []
      summary: Search talent profiles
      tags:
        - marketplace
servers:
  - url: /
components:
  requestBodies:
    app.flightDebriefUpsertDTO:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/app.flightDebriefUpsertDTO'
      description: Debrief payload
      required: true
    app.scimPatchRequest:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/app.scimPatchRequest'
      description: SCIM patch operations
      required: true
    app.dispatchTransitionRequest:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/app.dispatchTransitionRequest'
      description: Optional override + note
  securitySchemes:
    ApiKeyAuth:
      in: header
      name: X-API-Key
      type: apiKey
    BearerAuth:
      in: header
      name: Authorization
      type: apiKey
  schemas:
    app.AircraftDTO:
      properties:
        aircraft_type:
          type: string
        base:
          type: string
        registration:
          type: string
        status:
          description: available|unavailable|maintenance
          type: string
        tach_total:
          type: string
      type: object
    app.ChargeCreateDTO:
      properties:
        amount_cents:
          maximum: 1000000000
          minimum: 0
          type: integer
        category:
          maxLength: 128
          type: string
        enrollment_id:
          type: string
        note:
          maxLength: 4000
          type: string
        notes:
          maxLength: 4000
          type: string
        occurred_on:
          maxLength: 64
          type: string
        program_id:
          type: string
        reference:
          maxLength: 255
          type: string
        status:
          maxLength: 64
          type: string
        student_id:
          type: string
        student_person_id:
          type: string
      required:
        - amount_cents
        - category
        - occurred_on
      type: object
    app.DueItemCreateDTO:
      properties:
        due_date:
          description: YYYY-MM-DD
          type: string
        due_hours:
          description: 'numeric as string (ex: "1250.0")'
          type: string
        status:
          description: ok|upcoming|overdue|done
          type: string
        threshold_days:
          type: integer
        threshold_hours:
          type: string
        title:
          type: string
      type: object
    app.FlightCreateDTO:
      properties:
        aircraft_id:
          type: string
        alternate_icao:
          maxLength: 5
          type: string
        approval_status:
          maxLength: 64
          type: string
        arrival_icao:
          maxLength: 5
          type: string
        decision:
          maxLength: 64
          type: string
        departure_icao:
          maxLength: 5
          type: string
        end_time:
          maxLength: 64
          type: string
        ended_at:
          maxLength: 64
          type: string
        instructor_id:
          type: string
        offblock_time:
          maxLength: 64
          type: string
        onblock_time:
          maxLength: 64
          type: string
        overrideReason:
          type: string
        route_notes:
          maxLength: 500
          type: string
        start_time:
          maxLength: 64
          type: string
        started_at:
          maxLength: 64
          type: string
        status:
          maxLength: 64
          type: string
        student_id:
          type: string
        total_minutes:
          maximum: 100000
          minimum: 0
          type: integer
      type: object
    app.PaymentCreateDTO:
      properties:
        amount_cents:
          maximum: 1000000000
          minimum: 0
          type: integer
        enrollment_id:
          type: string
        method:
          maxLength: 64
          type: string
        notes:
          maxLength: 4000
          type: string
        paid_on:
          maxLength: 64
          type: string
        program_id:
          type: string
        reference:
          maxLength: 255
          type: string
        status:
          maxLength: 64
          type: string
        student_id:
          type: string
        student_person_id:
          type: string
      required:
        - amount_cents
        - method
        - paid_on
      type: object
    app.SyncPushItem:
      properties:
        entity_id:
          type: string
        entity_type:
          type: string
        op:
          type: string
        payload:
          type: object
      type: object
    app.SyncPushRequest:
      properties:
        changes:
          items:
            $ref: '#/components/schemas/app.SyncPushItem'
          type: array
      type: object
    app.WorkOrderCreateDTO:
      properties:
        assigned_to:
          type: string
        cost_actual_cents:
          type: integer
        cost_estimated_cents:
          type: integer
        description:
          type: string
        due_item_id:
          type: string
        priority:
          description: low|medium|high
          type: string
        status:
          description: open|in_progress|done|completed
          type: string
        title:
          type: string
      type: object
    app.WorkOrderPatchDTO:
      properties:
        assigned_to:
          type: string
        cost_actual_cents:
          type: integer
        cost_estimated_cents:
          type: integer
        description:
          type: string
        due_item_id:
          type: string
        expenseId:
          description: F1 extensions
          type: string
        groundsAircraft:
          type: boolean
        priority:
          type: string
        reserveDrawdown:
          properties:
            amountCents:
              type: integer
            reserveKind:
              type: string
          type: object
        status:
          type: string
        title:
          type: string
      type: object
    app.accountKindRequest:
      properties:
        email:
          maxLength: 320
          type: string
      required:
        - email
      type: object
    app.accountKindResponse:
      properties:
        kind:
          type: string
      type: object
    app.acknowledgeBlockerPayload:
      properties:
        acknowledged_text:
          type: string
        blocker_key:
          type: string
        compliance_record_id:
          type: string
        flight_id:
          type: string
        organization_id:
          type: string
      type: object
    app.acknowledgeRequest:
      properties:
        ack_text:
          type: string
        actor_name:
          type: string
        kind:
          type: string
      type: object
    app.acknowledgmentResponse:
      properties:
        acknowledged_at:
          type: string
        actor_name_snapshot:
          type: string
        id:
          type: string
        kind:
          type: string
      type: object
    app.aerodromeDocumentResponse:
      properties:
        availability_status:
          type: string
        cached_storage_path:
          type: string
        doc_type:
          type: string
        fetched_at:
          type: string
        icao:
          type: string
        id:
          type: string
        org_scoped:
          type: boolean
        source_name:
          type: string
        source_updated_at:
          type: string
        source_url:
          type: string
        title:
          type: string
      type: object
    app.aerodromeDocumentsForICAOResponse:
      properties:
        aerodrome:
          $ref: '#/components/schemas/app.aerodromeHintResponse'
        documents:
          items:
            $ref: '#/components/schemas/app.aerodromeDocumentResponse'
          type: array
      type: object
    app.aerodromeHintResponse:
      properties:
        country:
          type: string
        icao:
          type: string
        known_in_registry:
          description: |-
            KnownInRegistry tells the UI whether we have a curated entry for
            this ICAO. When false, the response payload will most likely have
            availability_status = "not_connected" and the UI should explain
            that we don't yet have an SIA reference for this aerodrome.
          type: boolean
        name:
          type: string
      type: object
    app.aircraftARCDTO:
      properties:
        aircraft_id:
          type: string
        arc_expires_at:
          type: string
        arc_last_renewed_at:
          type: string
        arc_notes:
          type: string
        days_until_expiry:
          type: integer
        registration:
          type: string
        status:
          description: unknown | ok | due_soon | due | overdue
          type: string
      type: object
    app.aircraftFlagsDTO:
      properties:
        aircraftId:
          type: string
        grounded:
          type: boolean
        hardDueOverdueCount:
          type: integer
        softDueCount:
          type: integer
      type: object
    app.aircraftPatchRequest:
      properties:
        aircraft_type:
          type: string
        base:
          type: string
        registration:
          type: string
        status:
          type: string
        tach_total:
          type: number
      type: object
    app.aircraftPerformancePackDTO:
      properties:
        confidence:
          type: string
        empty_arm_m:
          type: number
        empty_mass_kg:
          type: number
        fuel_burn_lph:
          type: number
        fuel_capacity_l:
          type: number
        fuel_density_kg_per_l:
          type: number
        mlw_kg:
          type: number
        mtow_kg:
          type: number
        notes:
          type: string
        updated_at:
          type: string
      type: object
    app.appDetailResponse:
      properties:
        app:
          $ref: '#/components/schemas/app.appRegistryResponse'
        installation:
          $ref: '#/components/schemas/app.appInstallationResponse'
      type: object
    app.appInstallationResponse:
      properties:
        app_id:
          type: string
        app_slug:
          type: string
        app_title:
          type: string
        disabled_at:
          type: string
        id:
          type: string
        installed_at:
          type: string
        installed_by_user_id:
          type: string
        linked_api_key_id:
          type: string
        linked_webhook_endpoint_id:
          type: string
        organization_id:
          type: string
        settings:
          additionalProperties: true
          type: object
        status:
          type: string
        uninstalled_at:
          type: string
        updated_at:
          type: string
      type: object
    app.appRegistryResponse:
      properties:
        capabilities:
          items:
            type: string
          type: array
        category:
          type: string
        created_at:
          type: string
        description:
          type: string
        events_used:
          items:
            type: string
          type: array
        id:
          type: string
        install_doc_url:
          type: string
        installable:
          description: |-
            Installable for the calling org? Computed server-side so the UI
            never has to second-guess the policy.
          type: boolean
        listed_at:
          type: string
        scopes_required:
          items:
            type: string
          type: array
        slug:
          type: string
        status:
          type: string
        summary:
          type: string
        title:
          type: string
        updated_at:
          type: string
        vendor:
          type: string
      type: object
    app.approvedProgramView:
      properties:
        chief_instructor_id:
          type: string
        created_at:
          type: string
        faa_school_number:
          type: string
        id:
          type: string
        name:
          type: string
        organization_id:
          type: string
        tco_document_id:
          type: string
        updated_at:
          type: string
      type: object
    app.automationRuleActionPayloadV1:
      properties:
        kind:
          type: string
        params:
          additionalProperties: true
          type: object
      type: object
    app.automationRuleConditionV1:
      properties:
        kind:
          type: string
        params:
          additionalProperties: true
          type: object
      type: object
    app.automationRuleCreateRequest:
      properties:
        actions:
          items:
            type: object
          type: array
        active:
          type: boolean
        code:
          maxLength: 128
          type: string
        conditions:
          items:
            type: object
          type: array
        config:
          type: object
        description:
          maxLength: 4000
          type: string
        enabled:
          type: boolean
        event_type:
          maxLength: 128
          type: string
        eventType:
          maxLength: 128
          type: string
        name:
          maxLength: 255
          type: string
        rule_type:
          maxLength: 64
          type: string
        schedule:
          maxLength: 128
          type: string
        schedule_cron:
          maxLength: 128
          type: string
        status:
          enum:
            - active
            - disabled
          type: string
        trigger:
          maxLength: 128
          type: string
        trigger_type:
          maxLength: 128
          type: string
        type:
          maxLength: 64
          type: string
      required:
        - name
      type: object
    app.automationRuleDryRunRequestV1:
      properties:
        actions:
          items:
            $ref: '#/components/schemas/app.automationRuleActionPayloadV1'
          type: array
        conditions:
          items:
            $ref: '#/components/schemas/app.automationRuleConditionV1'
          type: array
        description:
          maxLength: 4000
          type: string
        event_filters:
          additionalProperties: true
          type: object
        event_kind:
          maxLength: 128
          type: string
        event_type:
          maxLength: 128
          type: string
        eventType:
          maxLength: 128
          type: string
        name:
          maxLength: 255
          type: string
        rule:
          $ref: '#/components/schemas/app.automationRulePayloadV1'
        rule_id:
          type: string
        ruleId:
          type: string
        severity:
          enum:
            - info
            - warning
            - critical
          type: string
        status:
          enum:
            - active
            - disabled
          type: string
        stop_on_error:
          type: boolean
        trigger_kind:
          enum:
            - snapshot
            - event
          type: string
      required:
        - name
      type: object
    app.automationRulePatchPayloadV1:
      properties:
        actions:
          items:
            $ref: '#/components/schemas/app.automationRuleActionPayloadV1'
          type: array
        conditions:
          items:
            $ref: '#/components/schemas/app.automationRuleConditionV1'
          type: array
        description:
          maxLength: 4000
          type: string
        event_filters:
          additionalProperties: true
          type: object
        event_kind:
          maxLength: 128
          type: string
        event_type:
          maxLength: 128
          type: string
        eventType:
          maxLength: 128
          type: string
        name:
          maxLength: 255
          type: string
        severity:
          enum:
            - info
            - warning
            - critical
          type: string
        status:
          enum:
            - active
            - disabled
          type: string
        stop_on_error:
          type: boolean
        trigger_kind:
          enum:
            - snapshot
            - event
          type: string
      type: object
    app.automationRulePatchRequest:
      properties:
        actions:
          items:
            type: object
          type: array
        active:
          type: boolean
        code:
          maxLength: 128
          type: string
        conditions:
          items:
            type: object
          type: array
        config:
          type: object
        description:
          maxLength: 4000
          type: string
        enabled:
          type: boolean
        event_type:
          maxLength: 128
          type: string
        eventType:
          maxLength: 128
          type: string
        id:
          type: string
        name:
          maxLength: 255
          type: string
        rule_id:
          type: string
        rule_type:
          maxLength: 64
          type: string
        ruleId:
          type: string
        schedule:
          maxLength: 128
          type: string
        schedule_cron:
          maxLength: 128
          type: string
        status:
          enum:
            - active
            - disabled
          type: string
        trigger:
          maxLength: 128
          type: string
        trigger_type:
          maxLength: 128
          type: string
        type:
          maxLength: 64
          type: string
      type: object
    app.automationRulePayloadV1:
      properties:
        actions:
          items:
            $ref: '#/components/schemas/app.automationRuleActionPayloadV1'
          type: array
        conditions:
          items:
            $ref: '#/components/schemas/app.automationRuleConditionV1'
          type: array
        description:
          maxLength: 4000
          type: string
        event_filters:
          additionalProperties: true
          type: object
        event_kind:
          maxLength: 128
          type: string
        event_type:
          maxLength: 128
          type: string
        eventType:
          maxLength: 128
          type: string
        name:
          maxLength: 255
          type: string
        severity:
          enum:
            - info
            - warning
            - critical
          type: string
        status:
          enum:
            - active
            - disabled
          type: string
        stop_on_error:
          type: boolean
        trigger_kind:
          enum:
            - snapshot
            - event
          type: string
      required:
        - name
      type: object
    app.automationRuleToggleRequestV1:
      properties:
        enabled:
          type: boolean
        status:
          type: string
      type: object
    app.automationRuleValidateRequestV1:
      properties:
        actions:
          items:
            $ref: '#/components/schemas/app.automationRuleActionPayloadV1'
          type: array
        conditions:
          items:
            $ref: '#/components/schemas/app.automationRuleConditionV1'
          type: array
        description:
          maxLength: 4000
          type: string
        event_filters:
          additionalProperties: true
          type: object
        event_kind:
          maxLength: 128
          type: string
        event_type:
          maxLength: 128
          type: string
        eventType:
          maxLength: 128
          type: string
        name:
          maxLength: 255
          type: string
        rule:
          $ref: '#/components/schemas/app.automationRulePayloadV1'
        severity:
          enum:
            - info
            - warning
            - critical
          type: string
        status:
          enum:
            - active
            - disabled
          type: string
        stop_on_error:
          type: boolean
        trigger_kind:
          enum:
            - snapshot
            - event
          type: string
      required:
        - name
      type: object
    app.baseResponse:
      properties:
        created_at:
          type: string
        iata_code:
          type: string
        icao_code:
          type: string
        id:
          type: string
        is_primary:
          type: boolean
        name:
          type: string
        organization_id:
          type: string
        timezone:
          type: string
        updated_at:
          type: string
      type: object
    app.billingCheckoutRequest:
      properties:
        cancel_url:
          type: string
        price_id:
          type: string
        quantity:
          type: integer
        success_url:
          type: string
        trial_days:
          type: integer
      type: object
    app.billingPortalRequest:
      properties:
        return_url:
          type: string
      type: object
    app.bookingRequestResponse:
      properties:
        aircraft_id:
          type: string
        aircraft_registration:
          type: string
        aircraft_type:
          type: string
        created_at:
          type: string
        decision_at:
          type: string
        decision_reason:
          type: string
        decision_user_id:
          type: string
        guest_organization_id:
          type: string
        guest_organization_name:
          type: string
        host_organization_id:
          type: string
        host_organization_name:
          type: string
        id:
          type: string
        listing_id:
          type: string
        planning_event_id:
          type: string
        purpose:
          type: string
        requested_end_at:
          type: string
        requested_start_at:
          type: string
        requester_user_id:
          type: string
        status:
          type: string
        updated_at:
          type: string
        validation_warnings:
          items:
            type: string
          type: array
      type: object
    app.capabilitiesModules:
      properties:
        addon_marketplace:
          description: v2.1 Phase B5 — consumer install hub gate
          type: boolean
        automation_rules:
          type: boolean
        benchmarks:
          type: boolean
        copilot_chat:
          type: boolean
        developer_apps:
          description: v2.1 Phase B4 — builder hub gate
          type: boolean
        dispatch_v2:
          type: boolean
        forecasts:
          type: boolean
        ops_brain_v1:
          type: boolean
        planning:
          type: boolean
        platform_admin_console:
          description: v2.1 Phase B5 — platform admin review-queue gate
          type: boolean
        policy_engine_v1:
          type: boolean
      type: object
    app.capabilitiesResponse:
      properties:
        build_time:
          type: string
        modules:
          $ref: '#/components/schemas/app.capabilitiesModules'
        version:
          type: string
      type: object
    app.chargePatchDTO:
      properties:
        amount_cents:
          type: integer
        category:
          type: string
        enrollment_id:
          type: string
        note:
          type: string
        notes:
          type: string
        program_id:
          type: string
        reference:
          type: string
        status:
          type: string
        student_id:
          type: string
        student_person_id:
          type: string
      type: object
    app.claimOrgInviteRequest:
      properties:
        full_name:
          maxLength: 200
          type: string
        fullName:
          maxLength: 200
          type: string
        password:
          maxLength: 512
          minLength: 1
          type: string
        token:
          maxLength: 2048
          minLength: 1
          type: string
      required:
        - token
      type: object
    app.claimStudentInviteRequest:
      properties:
        full_name:
          maxLength: 200
          type: string
        fullName:
          maxLength: 200
          type: string
        password:
          maxLength: 512
          minLength: 8
          type: string
        token:
          maxLength: 2048
          minLength: 10
          type: string
      required:
        - token
      type: object
    app.complianceCalendarEntry:
      properties:
        aircraft_id:
          type: string
        aircraft_registration:
          type: string
        category:
          type: string
        compliance_item_id:
          type: string
        currency_actual:
          type: integer
        currency_required:
          type: integer
        currency_rule_id:
          type: string
        days_until:
          type: integer
        due_date:
          type: string
        forecast_expires_at:
          type: string
        handling:
          $ref: '#/components/schemas/app.complianceCalendarHandlingSummary'
        id:
          type: string
        item_code:
          type: string
        item_name:
          type: string
        person_id:
          type: string
        person_name:
          type: string
        person_type:
          type: string
        record_id:
          type: string
        reference:
          type: string
        renewable:
          type: boolean
        review_state:
          description: |-
            ReviewState mirrors person_compliance_records.review_state when
            the record is part of a renewal workflow. Populated for record
            entries only (nil for missing/currency). Lets the console render
            a "pending review" badge and a "Review" CTA.
          type: string
        rule_code:
          description: |-
            Phase 19 Plan 01 — FCL rule attribution. RuleCode is the
            fcl_rules.rule_code that produced this record (NULL for legacy
            records and operator-entered documents). ForecastExpiresAt is the
            fcl.ForecastNextExpiry predicted transition timestamp (distinct from
            DueDate which is the certificate's paper expiry). Both feed Phase 20
            pilot dashboard tooltips + the rule-detail drawer.
          type: string
        severity:
          type: string
        source:
          type: string
        state:
          type: string
        subject_kind:
          description: |-
            Phase C — subject kind. "person" for the existing entries (default
            when omitted by the legacy builder); "aircraft" for entries
            produced by loadAircraftCalendarEntries. Aircraft entries leave
            PersonID/PersonName/PersonType blank and populate AircraftID +
            AircraftRegistration instead.
          type: string
      type: object
    app.complianceCalendarHandleDeleteRequest:
      properties:
        compliance_item_id:
          type: string
        person_id:
          type: string
      type: object
    app.complianceCalendarHandleRequest:
      properties:
        compliance_item_id:
          type: string
        note:
          type: string
        owner_user_id:
          description: |-
            OwnerUserID assigns a responsible user to this handling. Optional.
            When present the handler validates the user is a member of the org.
            Send "" or omit to leave unchanged on an update ; send null to clear
            is not supported via the partial update — clear by re-upserting
            without the field or via /handle DELETE. v1.7 Phase 15 Plan 01.
          type: string
        person_id:
          type: string
        record_id:
          type: string
        snooze_until:
          type: string
        state:
          type: string
      type: object
    app.complianceCalendarHandlingSummary:
      properties:
        note:
          type: string
        owner_display_name:
          type: string
        owner_user_id:
          description: |-
            OwnerUserID + OwnerDisplayName are surfaced when an owner is set on
            the handling. Console renders an owner badge per entry and supports
            an "Owned by me" filter from these fields. v1.7 Phase 15 Plan 01.
          type: string
        snooze_until:
          type: string
        state:
          type: string
        updated_at:
          type: string
      type: object
    app.complianceCalendarResult:
      properties:
        entries:
          items:
            $ref: '#/components/schemas/app.complianceCalendarEntry'
          type: array
        generated_at:
          type: string
        lookahead_days:
          type: integer
        organization_id:
          type: string
        soon_threshold_days:
          type: integer
        stats:
          $ref: '#/components/schemas/app.complianceCalendarStats'
        truncated:
          type: boolean
      type: object
    app.complianceCalendarStats:
      properties:
        by_category:
          additionalProperties:
            type: integer
          type: object
        by_severity:
          additionalProperties:
            type: integer
          type: object
        by_state:
          additionalProperties:
            type: integer
          type: object
        due_in_seven_days:
          type: integer
        expired_count:
          type: integer
        handled_count:
          type: integer
        missing_count:
          type: integer
        total:
          type: integer
      type: object
    app.complianceDigestPrefDTO:
      properties:
        cadence:
          type: string
        locale:
          type: string
        weekday:
          type: integer
      type: object
    app.complianceItemCreateRequest:
      properties:
        active:
          type: boolean
        category:
          type: string
        code:
          type: string
        default_severity:
          type: string
        default_validity_days:
          type: integer
        description:
          type: string
        metadata:
          type: object
        name:
          type: string
        requires_expiry:
          type: boolean
        validity_days:
          type: integer
      type: object
    app.complianceItemResponse:
      properties:
        active:
          type: boolean
        category:
          type: string
        code:
          type: string
        created_at:
          type: string
        default_severity:
          type: string
        default_validity_days:
          type: integer
        description:
          type: string
        id:
          type: string
        metadata:
          type: object
        name:
          type: string
        requires_expiry:
          type: boolean
        updated_at:
          type: string
        validity_days:
          type: integer
      type: object
    app.compliancePersonResponse:
      properties:
        created_at:
          type: string
        email:
          type: string
        external_ref:
          type: string
        first_name:
          type: string
        id:
          type: string
        last_name:
          type: string
        status:
          type: string
        type:
          type: string
        updated_at:
          type: string
      type: object
    app.complianceStatsDTO:
      properties:
        currency:
          type: string
        groundedCount:
          type: integer
        hardDueOverdueCount:
          type: integer
        openCriticalAdSbCount:
          type: integer
        totalReserveBalanceCents:
          type: integer
      type: object
    app.copilotChatRequest:
      properties:
        message:
          type: string
        thread_id:
          type: string
        threadId:
          type: string
      type: object
    app.createApprovedProgramRequest:
      properties:
        chief_instructor_id:
          description: people.id
          type: string
        faa_school_number:
          type: string
        name:
          type: string
      type: object
    app.createApprovedProgramResponse:
      properties:
        chief_signoff:
          $ref: '#/components/schemas/app.signoffRefView'
        program:
          $ref: '#/components/schemas/app.approvedProgramView'
      type: object
    app.createBaseRequest:
      properties:
        iata_code:
          maxLength: 10
          type: string
        icao_code:
          maxLength: 10
          type: string
        name:
          maxLength: 200
          type: string
        timezone:
          maxLength: 100
          type: string
      required:
        - name
      type: object
    app.createBookingRequestPayload:
      properties:
        listing_id:
          type: string
        purpose:
          type: string
        requested_end_at:
          type: string
        requested_start_at:
          type: string
      type: object
    app.createMaintenanceProgramRequest:
      properties:
        active:
          type: boolean
        description:
          type: string
        items:
          items:
            $ref: '#/components/schemas/app.maintenanceProgramItemDTO'
          type: array
        name:
          type: string
      type: object
    app.createStageCheckRequest:
      properties:
        notes:
          type: string
        passed:
          type: boolean
        student_person_id:
          description: people.id of the student
          type: string
      type: object
    app.createStageCheckResponse:
      properties:
        stage_check_id:
          type: string
        student_signoff:
          $ref: '#/components/schemas/app.signoffRefView'
      type: object
    app.createTCODocumentRequest:
      properties:
        document_url:
          type: string
        faa_approval_date:
          description: ISO date (YYYY-MM-DD)
          type: string
        faa_approval_letter_url:
          description: optional, allowlisted
          type: string
        version:
          type: string
      type: object
    app.createTCODocumentResponse:
      properties:
        document:
          $ref: '#/components/schemas/app.tcoDocumentView'
        tco_document_id:
          description: also patched onto approved_programs.tco_document_id
          type: string
      type: object
    app.currencyRuleResponse:
      properties:
        active:
          type: boolean
        applies_to:
          type: object
        code:
          type: string
        created_at:
          type: string
        description:
          type: string
        event_type:
          type: string
        id:
          type: string
        metadata:
          type: object
        name:
          type: string
        required_count:
          type: integer
        updated_at:
          type: string
        window_days:
          type: integer
      type: object
    app.customerUpdateRequest:
      properties:
        email:
          maxLength: 320
          type: string
        name:
          maxLength: 200
          type: string
        phone:
          maxLength: 50
          type: string
      type: object
    app.dayBoardAircraft:
      properties:
        arc_expires_at:
          type: string
        id:
          type: string
        open_due_items_count:
          type: integer
        registration:
          type: string
        status:
          type: string
      type: object
    app.dayBoardApprovals:
      properties:
        pending_count:
          type: integer
      type: object
    app.dayBoardBriefingStats:
      properties:
        full:
          type: integer
        missing:
          type: integer
        partial:
          type: integer
      type: object
    app.dayBoardFleet:
      properties:
        counts:
          additionalProperties:
            type: integer
          type: object
        items:
          items:
            $ref: '#/components/schemas/app.dayBoardAircraft'
          type: array
      type: object
    app.dayBoardFlight:
      properties:
        approval_status:
          type: string
        ended_at:
          type: string
        id:
          type: string
        instructor_name:
          type: string
        ops_status:
          type: string
        registration:
          type: string
        started_at:
          type: string
        student_name:
          type: string
      type: object
    app.dayBoardFlights:
      properties:
        by_ops_status:
          additionalProperties:
            items:
              $ref: '#/components/schemas/app.dayBoardFlight'
            type: array
          type: object
        counts:
          additionalProperties:
            type: integer
          type: object
        late_returns:
          items:
            $ref: '#/components/schemas/app.dayBoardLateReturn'
          type: array
      type: object
    app.dayBoardInstructor:
      properties:
        name:
          type: string
        person_id:
          type: string
      type: object
    app.dayBoardInstructorStats:
      properties:
        available_count:
          type: integer
        items:
          items:
            $ref: '#/components/schemas/app.dayBoardInstructor'
          type: array
      type: object
    app.dayBoardLateReturn:
      properties:
        expected_return_at:
          type: string
        flight_id:
          type: string
        minutes_late:
          type: integer
        registration:
          type: string
      type: object
    app.dayBoardMaintenance:
      properties:
        due_items_overdue_count:
          type: integer
        open_work_orders_count:
          type: integer
      type: object
    app.dayBoardPeople:
      properties:
        instructors:
          $ref: '#/components/schemas/app.dayBoardInstructorStats'
        students:
          $ref: '#/components/schemas/app.dayBoardStudentStats'
      type: object
    app.dayBoardResponse:
      properties:
        approvals:
          $ref: '#/components/schemas/app.dayBoardApprovals'
        briefing_completeness:
          $ref: '#/components/schemas/app.dayBoardBriefingStats'
        date:
          type: string
        fleet:
          $ref: '#/components/schemas/app.dayBoardFleet'
        flights:
          $ref: '#/components/schemas/app.dayBoardFlights'
        generated_at:
          type: string
        maintenance:
          $ref: '#/components/schemas/app.dayBoardMaintenance'
        people:
          $ref: '#/components/schemas/app.dayBoardPeople'
        tz:
          type: string
        weather_snapshot:
          $ref: '#/components/schemas/app.dayBoardWeather'
      type: object
    app.dayBoardStudent:
      properties:
        name:
          type: string
        person_id:
          type: string
        reason:
          type: string
      type: object
    app.dayBoardStudentStats:
      properties:
        active_count:
          type: integer
        blocked_count:
          type: integer
        items_blocked:
          items:
            $ref: '#/components/schemas/app.dayBoardStudent'
          type: array
      type: object
    app.dayBoardWeather:
      properties:
        source:
          type: string
        stale:
          type: boolean
      type: object
    app.devSampleNotificationRequest:
      properties:
        recipient_user_id:
          type: string
      type: object
    app.directoryAdminPatchRequest:
      properties:
        featured:
          type: boolean
        sort_priority:
          type: integer
        status:
          type: string
      type: object
    app.directoryAircraftItem:
      properties:
        available_for_rental:
          type: boolean
        avionics:
          items:
            type: string
          type: array
        category:
          type: string
        city:
          type: string
        contact_email:
          type: string
        country:
          type: string
        created_at:
          type: string
        description:
          type: string
        featured:
          type: boolean
        hourly_rate:
          type: string
        icao:
          type: string
        id:
          type: string
        ifr:
          type: boolean
        last_maintenance:
          type: string
        lat:
          type: number
        lng:
          type: number
        model:
          type: string
        photo_url:
          type: string
        registration:
          type: string
        school_id:
          type: string
        slug:
          type: string
        total_time:
          type: integer
        type:
          type: string
        website:
          type: string
        year_built:
          type: integer
      type: object
    app.directoryInstructorItem:
      properties:
        active_since:
          type: integer
        bio:
          type: string
        city:
          type: string
        contact_email:
          type: string
        country:
          type: string
        created_at:
          type: string
        featured:
          type: boolean
        first_name:
          type: string
        hours_total:
          type: integer
        icao:
          type: string
        id:
          type: string
        languages:
          items:
            type: string
          type: array
        last_name:
          type: string
        lat:
          type: number
        lng:
          type: number
        photo_url:
          type: string
        pricing_range:
          type: string
        ratings:
          items:
            type: string
          type: array
        school_id:
          type: string
        slug:
          type: string
        training_for:
          items:
            type: string
          type: array
        trust_level:
          type: integer
        website:
          type: string
      type: object
    app.directoryListResponse:
      properties:
        data: {}
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
      type: object
    app.directorySchoolItem:
      properties:
        accepting_enrollments:
          type: boolean
        active_programs_count:
          type: integer
        active_since:
          type: integer
        aircraft_types:
          items:
            type: string
          type: array
        city:
          type: string
        contact_email:
          type: string
        country:
          type: string
        created_at:
          type: string
        description:
          type: string
        featured:
          type: boolean
        fleet_size:
          type: integer
        icao:
          type: string
        id:
          type: string
        languages:
          items:
            type: string
          type: array
        lat:
          type: number
        lng:
          type: number
        logo_url:
          type: string
        name:
          type: string
        partner_cta_label:
          type: string
        partner_cta_url:
          type: string
        partner_highlight:
          type: string
        pricing_range:
          type: string
        programs: {}
        ratings:
          items:
            type: string
          type: array
        slug:
          type: string
        trust_level:
          type: integer
        type:
          type: string
        website:
          type: string
      type: object
    app.directorySubmitAircraftRequest:
      properties:
        available_for_rental:
          type: boolean
        avionics:
          items:
            type: string
          type: array
        category:
          type: string
        city:
          type: string
        contact_email:
          type: string
        country:
          type: string
        description:
          type: string
        hourly_rate:
          type: string
        icao:
          type: string
        ifr:
          type: boolean
        last_maintenance:
          type: string
        lat:
          type: number
        lng:
          type: number
        model:
          type: string
        photo_url:
          type: string
        registration:
          type: string
        school_id:
          type: string
        total_time:
          type: integer
        type:
          type: string
        website:
          type: string
        year_built:
          type: integer
      type: object
    app.directorySubmitInstructorRequest:
      properties:
        active_since:
          type: integer
        bio:
          type: string
        city:
          type: string
        contact_email:
          type: string
        country:
          type: string
        first_name:
          type: string
        hours_total:
          type: integer
        icao:
          type: string
        languages:
          items:
            type: string
          type: array
        last_name:
          type: string
        lat:
          type: number
        lng:
          type: number
        photo_url:
          type: string
        pricing_range:
          type: string
        ratings:
          items:
            type: string
          type: array
        school_id:
          type: string
        training_for:
          items:
            type: string
          type: array
        website:
          type: string
      type: object
    app.directorySubmitResponse:
      properties:
        edit_token:
          type: string
        id:
          type: string
        slug:
          type: string
        status:
          type: string
      type: object
    app.directorySubmitSchoolRequest:
      properties:
        accepting_enrollments:
          type: boolean
        active_programs_count:
          type: integer
        active_since:
          type: integer
        aircraft_types:
          items:
            type: string
          type: array
        city:
          type: string
        contact_email:
          type: string
        country:
          type: string
        description:
          type: string
        fleet_size:
          type: integer
        icao:
          type: string
        languages:
          items:
            type: string
          type: array
        lat:
          type: number
        lng:
          type: number
        logo_url:
          type: string
        name:
          type: string
        pricing_range:
          type: string
        programs: {}
        ratings:
          items:
            type: string
          type: array
        type:
          type: string
        website:
          type: string
      type: object
    app.dispatchAppliedOverride:
      properties:
        applied_at:
          type: string
        applied_by:
          type: string
        reason:
          type: string
        rule_code:
          type: string
      type: object
    app.dispatchDecisionListItem:
      properties:
        blockers_at_decision:
          items:
            $ref: '#/components/schemas/app.dispatchRuleResponse'
          type: array
        decided_at:
          type: string
        decided_by_user_id:
          type: string
        id:
          type: string
        outcome:
          type: string
        overrides_applied:
          items:
            $ref: '#/components/schemas/app.dispatchAppliedOverride'
          type: array
        transition_from:
          type: string
        transition_to:
          type: string
      type: object
    app.dispatchDecisionListResponse:
      properties:
        items:
          items:
            $ref: '#/components/schemas/app.dispatchDecisionListItem'
          type: array
      type: object
    app.dispatchDecisionResponse:
      properties:
        blockers_at_decision:
          items:
            $ref: '#/components/schemas/app.dispatchRuleResponse'
          type: array
        decided_at:
          type: string
        id:
          type: string
        outcome:
          type: string
        overrides_applied:
          items:
            $ref: '#/components/schemas/app.dispatchAppliedOverride'
          type: array
      type: object
    app.dispatchFlightSummary:
      properties:
        id:
          type: string
        ops_status:
          type: string
      type: object
    app.dispatchGateResponse:
      properties:
        blockers:
          items:
            $ref: '#/components/schemas/app.dispatchRuleResponse'
          type: array
        evaluated_at:
          type: string
        incomplete:
          items:
            $ref: '#/components/schemas/app.dispatchRuleResponse'
          type: array
        outcome:
          type: string
        rules_evaluated:
          items:
            $ref: '#/components/schemas/app.dispatchRuleResponse'
          type: array
        target_ops_status:
          type: string
        warnings:
          items:
            $ref: '#/components/schemas/app.dispatchRuleResponse'
          type: array
      type: object
    app.dispatchOverrideJustificationDTO:
      properties:
        reason:
          type: string
        rule_code:
          type: string
      type: object
    app.dispatchPolicyItem:
      properties:
        enabled:
          type: boolean
        rule_code:
          type: string
        severity:
          type: string
        updated_at:
          type: string
      type: object
    app.dispatchPolicyPutItem:
      properties:
        enabled:
          type: boolean
        rule_code:
          type: string
        severity:
          type: string
      type: object
    app.dispatchPolicyPutRequest:
      properties:
        items:
          items:
            $ref: '#/components/schemas/app.dispatchPolicyPutItem'
          type: array
      type: object
    app.dispatchPolicyResponse:
      properties:
        configurable_rules:
          items:
            type: string
          type: array
        items:
          items:
            $ref: '#/components/schemas/app.dispatchPolicyItem'
          type: array
      type: object
    app.dispatchRuleResponse:
      properties:
        category:
          type: string
        code:
          type: string
        evidence:
          additionalProperties: {}
          type: object
        message:
          type: string
        overridable:
          type: boolean
        override_permission:
          type: string
        severity:
          type: string
        status:
          type: string
      type: object
    app.dispatchTransitionRequest:
      properties:
        note:
          type: string
        override:
          properties:
            justifications:
              items:
                $ref: '#/components/schemas/app.dispatchOverrideJustificationDTO'
              type: array
          type: object
        reason:
          type: string
      type: object
    app.dispatchTransitionResponse:
      properties:
        decision:
          $ref: '#/components/schemas/app.dispatchDecisionResponse'
        flight:
          $ref: '#/components/schemas/app.dispatchFlightSummary'
      type: object
    app.driftAckRequest:
      properties:
        note:
          type: string
      type: object
    app.enrollmentUpsertRequest:
      properties:
        contract:
          type: object
        currency:
          type: string
        expected_total_cost_cents:
          type: integer
        planned_total_cents:
          type: integer
        program_id:
          type: string
        start_date:
          type: string
        status:
          type: string
        student_id:
          type: string
      type: object
    app.establishExchangeRequest:
      properties:
        token:
          type: string
      type: object
    app.establishTokenRequest:
      properties:
        account_type:
          type: string
      type: object
    app.expenseCategoryCreateDTO:
      properties:
        code:
          maxLength: 64
          type: string
        label:
          maxLength: 200
          type: string
        sort_order:
          maximum: 10000
          minimum: 0
          type: integer
      required:
        - code
        - label
      type: object
    app.fdmEventResponse:
      properties:
        created_at:
          type: string
        detail:
          type: string
        id:
          type: string
        kind:
          type: string
        occurred_at:
          type: string
        severity:
          type: string
        supporting_values:
          additionalProperties: {}
          type: object
        title:
          type: string
      type: object
    app.fdmFlightResponse:
      properties:
        aircraft_id:
          type: string
        aircraft_registration:
          type: string
        altitude_trace:
          items:
            additionalProperties: {}
            type: object
          type: array
        completeness:
          type: string
        created_at:
          type: string
        duration_minutes:
          type: integer
        end_at:
          type: string
        id:
          type: string
        ingest_id:
          type: string
        link_status:
          description: '"linked" | "candidate_aircraft_only" | "unmatched"'
          type: string
        max_altitude_ft:
          type: integer
        max_groundspeed_kt:
          type: integer
        max_oat_c:
          type: number
        quality_warnings:
          items:
            type: string
          type: array
        sample_count:
          type: integer
        start_at:
          type: string
        training_flight_id:
          type: string
        updated_at:
          type: string
      type: object
    app.fdmIngestDetailResponse:
      properties:
        events:
          items:
            $ref: '#/components/schemas/app.fdmEventResponse'
          type: array
        flight:
          $ref: '#/components/schemas/app.fdmFlightResponse'
        ingest:
          $ref: '#/components/schemas/app.fdmIngestResponse'
      type: object
    app.fdmIngestResponse:
      properties:
        created_at:
          type: string
        file_size_bytes:
          type: integer
        id:
          type: string
        original_filename:
          type: string
        parse_error:
          type: string
        parsed_at:
          type: string
        source_format:
          type: string
        status:
          type: string
        updated_at:
          type: string
      type: object
    app.flightBriefingInputsDTO:
      properties:
        additional_notes:
          type: string
        alternate_icao:
          type: string
        baggage_kg:
          type: number
        copilot_mass_kg:
          type: number
        fuel_onboard_l:
          type: number
        pilot_mass_kg:
          type: number
        rear_left_mass_kg:
          type: number
        rear_right_mass_kg:
          type: number
        reserve_minutes:
          type: integer
      type: object
    app.flightDebriefFDMEvent:
      properties:
        detail:
          type: string
        kind:
          type: string
        severity:
          type: string
        title:
          type: string
      type: object
    app.flightDebriefFDMSignals:
      properties:
        completeness:
          type: string
        duration_minutes:
          type: integer
        events:
          items:
            $ref: '#/components/schemas/app.flightDebriefFDMEvent'
          type: array
        fdm_flight_id:
          type: string
        max_altitude_ft:
          type: integer
        max_groundspeed_kt:
          type: integer
        sample_count:
          type: integer
      type: object
    app.flightDebriefResponse:
      properties:
        checklist:
          additionalProperties:
            type: boolean
          type: object
        completed_at:
          type: string
        completed_by_user_id:
          type: string
        created_at:
          type: string
        flight_id:
          type: string
        instructor_notes:
          type: string
        linked_signoff_ids:
          items:
            type: string
          type: array
        outcome:
          type: string
        recommendation:
          type: string
        state:
          type: string
        updated_at:
          type: string
      type: object
    app.flightDebriefSignals:
      properties:
        billing_amount_cents:
          type: integer
        billing_applied:
          type: boolean
        billing_currency:
          type: string
        effects_applied:
          type: boolean
        effects_applied_at:
          type: string
        fdm:
          $ref: '#/components/schemas/app.flightDebriefFDMSignals'
        flight_approved:
          type: boolean
        flight_status:
          type: string
        logbook_entry_id:
          type: string
        logbook_signed:
          type: boolean
        open_signoffs:
          items:
            additionalProperties: true
            type: object
          type: array
        student_id:
          type: string
      type: object
    app.flightDebriefUpsertDTO:
      properties:
        checklist:
          additionalProperties:
            type: boolean
          type: object
        instructor_notes:
          type: string
        linked_signoff_ids:
          items:
            type: string
          type: array
        outcome:
          type: string
        recommendation:
          type: string
        state:
          type: string
      type: object
    app.flightDebriefView:
      properties:
        debrief:
          $ref: '#/components/schemas/app.flightDebriefResponse'
        signals:
          $ref: '#/components/schemas/app.flightDebriefSignals'
      type: object
    app.flightDecisionDTO:
      properties:
        approval_status:
          enum:
            - submitted
            - pending_validation
            - pending
            - pending_approval
            - approved
            - rejected
          type: string
        decision:
          enum:
            - submitted
            - pending_validation
            - pending
            - pending_approval
            - approved
            - rejected
          type: string
        status:
          enum:
            - submitted
            - pending_validation
            - pending
            - pending_approval
            - approved
            - rejected
          type: string
      type: object
    app.flightPatchDTO:
      properties:
        aircraft_id:
          type: string
        alternate_icao:
          type: string
        approval_status:
          type: string
        arrival_icao:
          type: string
        block_delta_hours:
          maximum: 99.99
          minimum: 0
          type: number
        cycles_count:
          maximum: 999
          minimum: 0
          type: integer
        decision:
          type: string
        departure_icao:
          type: string
        end_time:
          type: string
        ended_at:
          type: string
        instructor_id:
          type: string
        landings_count:
          maximum: 99
          minimum: 0
          type: integer
        notes:
          type: string
        offblock_time:
          type: string
        onblock_time:
          type: string
        route_notes:
          type: string
        start_time:
          type: string
        started_at:
          type: string
        status:
          type: string
        student_id:
          type: string
        tach_delta_hours:
          maximum: 99.99
          minimum: 0
          type: number
        total_minutes:
          type: integer
      type: object
    app.idpConfigCreateRequest:
      properties:
        audience:
          type: string
        authorization_url:
          type: string
        client_id:
          type: string
        client_secret:
          type: string
        enabled:
          type: boolean
        entity_id:
          type: string
        issuer:
          type: string
        jwks_url:
          type: string
        metadata:
          type: object
        metadata_url:
          type: string
        name:
          type: string
        provider_type:
          type: string
        scopes:
          items:
            type: string
          type: array
        sso_url:
          type: string
        token_url:
          type: string
        type:
          type: string
      type: object
    app.idpConfigPatchRequest:
      properties:
        audience:
          type: string
        authorization_url:
          type: string
        client_id:
          type: string
        client_secret:
          type: string
        enabled:
          type: boolean
        entity_id:
          type: string
        id:
          type: string
        issuer:
          type: string
        jwks_url:
          type: string
        metadata:
          type: object
        metadata_url:
          type: string
        name:
          type: string
        provider_id:
          type: string
        provider_type:
          type: string
        providerId:
          type: string
        scopes:
          items:
            type: string
          type: array
        sso_url:
          type: string
        token_url:
          type: string
        type:
          type: string
      type: object
    app.installAppRequestPayload:
      properties:
        settings:
          items:
            type: integer
          type: array
      type: object
    app.leaveOrganizationRequest:
      properties:
        reason:
          type: string
      type: object
    app.listApprovedProgramsResponse:
      properties:
        programs:
          items:
            $ref: '#/components/schemas/app.approvedProgramView'
          type: array
      type: object
    app.listProgramStagesResponse:
      properties:
        stages:
          items:
            $ref: '#/components/schemas/app.programStageView'
          type: array
      type: object
    app.listTCODocumentsResponse:
      properties:
        documents:
          items:
            $ref: '#/components/schemas/app.tcoDocumentView'
          type: array
      type: object
    app.logbookCreateRequest:
      properties:
        aircraft_id:
          type: string
        aircraft_registration:
          type: string
        arrival_icao:
          type: string
        departure_icao:
          type: string
        duration_cross_country:
          type: number
        duration_dual:
          type: number
        duration_instrument:
          type: number
        duration_night:
          type: number
        duration_pic:
          type: number
        duration_solo:
          type: number
        duration_total:
          type: number
        enrollment_id:
          type: string
        exercises:
          items:
            type: string
          type: array
        flight_date:
          description: YYYY-MM-DD
          type: string
        flight_type:
          type: string
        instructor_id:
          type: string
        milestone_id:
          type: string
        notes_instructor:
          type: string
        notes_student:
          type: string
        ops_flight_id:
          type: string
        planning_event_id:
          type: string
      type: object
    app.loginRequest:
      properties:
        email:
          maxLength: 320
          type: string
        password:
          maxLength: 512
          minLength: 1
          type: string
      required:
        - email
        - password
      type: object
    app.maintenanceProgramDTO:
      properties:
        active:
          type: boolean
        aircraft_id:
          type: string
        created_at:
          type: string
        description:
          type: string
        id:
          type: string
        items:
          items:
            $ref: '#/components/schemas/app.maintenanceProgramItemDTO'
          type: array
        name:
          type: string
        updated_at:
          type: string
      type: object
    app.maintenanceProgramItemDTO:
      properties:
        description:
          type: string
        id:
          type: string
        interval_days:
          type: integer
        interval_hours:
          type: number
        interval_type:
          type: string
        item_type:
          type: string
        sort_order:
          type: integer
        title:
          type: string
        tolerance_days:
          type: integer
        tolerance_hours:
          type: number
      type: object
    app.mePaymentCheckoutRequest:
      properties:
        cancel_url:
          type: string
        invoice_id:
          type: string
        success_url:
          type: string
      type: object
    app.membershipCreateRequest:
      properties:
        email:
          maxLength: 320
          type: string
        membership_role:
          enum:
            - owner
            - admin
            - instructor
            - member
            - staff
          type: string
        membershipRole:
          enum:
            - owner
            - admin
            - instructor
            - member
            - staff
          type: string
        role:
          enum:
            - owner
            - admin
            - instructor
            - member
            - staff
          type: string
      required:
        - email
      type: object
    app.membershipPatchRequest:
      properties:
        membership_role:
          type: string
        membershipRole:
          type: string
        role:
          type: string
      type: object
    app.mfaEnrollStartRequest:
      properties:
        account:
          maxLength: 320
          type: string
        issuer:
          description: |-
            Issuer/account labels are optional cosmetic fields that show up
            in the authenticator app. We default to "GA Flight" + the user's
            email so the client doesn't have to pass anything.
          maxLength: 64
          type: string
      type: object
    app.mfaEnrollVerifyRequest:
      properties:
        code:
          maxLength: 12
          minLength: 6
          type: string
      required:
        - code
      type: object
    app.mfaStepRequest:
      properties:
        code:
          maxLength: 12
          minLength: 6
          type: string
      required:
        - code
      type: object
    app.mfaVerifyRequest:
      properties:
        code:
          type: string
        mfa_token:
          type: string
      required:
        - code
        - mfa_token
      type: object
    app.noShowPolicyPatchRequest:
      properties:
        auto_charge_enabled:
          type: boolean
        late_cancel_hours_threshold:
          type: integer
        late_cancel_penalty_pct:
          type: integer
        no_show_penalty_pct:
          type: integer
        very_late_cancel_hours_threshold:
          type: integer
        very_late_cancel_penalty_pct:
          type: integer
      type: object
    app.notamCreateDTO:
      properties:
        ends_at:
          type: string
        icao:
          type: string
        reference:
          type: string
        starts_at:
          type: string
        summary:
          type: string
      type: object
    app.notamItemDTO:
      properties:
        created_at:
          type: string
        ends_at:
          type: string
        icao:
          type: string
        id:
          type: string
        reference:
          type: string
        source:
          type: string
        starts_at:
          type: string
        summary:
          type: string
        updated_at:
          type: string
      type: object
    app.notificationTemplateCreateRequest:
      properties:
        body:
          type: string
        body_html:
          type: string
        body_text:
          type: string
        channel:
          type: string
        code:
          type: string
        name:
          type: string
        subject:
          type: string
      type: object
    app.notificationTemplatePatchRequest:
      properties:
        body:
          type: string
        body_html:
          type: string
        body_text:
          type: string
        channel:
          type: string
        code:
          type: string
        id:
          type: string
        name:
          type: string
        subject:
          type: string
        template_id:
          type: string
        templateId:
          type: string
      type: object
    app.oauthStartRequest:
      properties:
        account_type:
          type: string
        provider:
          type: string
        return_to:
          type: string
      type: object
    app.openapiAuthTokenPairResponse:
      properties:
        access_token:
          type: string
        expires_in:
          examples:
            - 900
          type: integer
        refresh_token:
          type: string
        token_type:
          examples:
            - bearer
          type: string
      type: object
    app.openapiComplianceBlockedErrorResponse:
      properties:
        code:
          examples:
            - COMPLIANCE_BLOCKED
          type: string
        message:
          examples:
            - request blocked by compliance policy
          type: string
        reasons:
          items:
            $ref: '#/components/schemas/app.openapiPolicyReason'
          type: array
      type: object
    app.openapiDeleteEnvelope:
      properties:
        deleted:
          type: boolean
        id:
          type: string
      type: object
    app.openapiErrorEnvelope:
      properties:
        code:
          examples:
            - FORBIDDEN
          type: string
        message:
          examples:
            - forbidden
          type: string
        request_id:
          examples:
            - a1b2c3d4e5f6
          type: string
      type: object
    app.openapiFieldError:
      properties:
        code:
          type: string
        field:
          type: string
        message:
          type: string
      type: object
    app.openapiIDEnvelope:
      properties:
        id:
          type: string
      type: object
    app.openapiMaintenanceWorkOrdersListResponse:
      properties:
        items:
          items:
            additionalProperties: true
            type: object
          type: array
        work_orders:
          items:
            additionalProperties: true
            type: object
          type: array
      type: object
    app.openapiMeResponse:
      properties:
        email:
          type: string
        full_name:
          type: string
        id:
          type: string
        is_platform_admin:
          type: boolean
        memberships:
          items:
            $ref: '#/components/schemas/app.openapiMembership'
          type: array
        student_portal:
          $ref: '#/components/schemas/app.openapiStudentPortalAccess'
      type: object
    app.openapiMembership:
      properties:
        instructor_person_id:
          type: string
        organization_id:
          type: string
        organization_name:
          type: string
        role:
          type: string
      type: object
    app.openapiOrganizationResponse:
      properties:
        address:
          type: string
        billing_email:
          type: string
        created_at:
          type: string
        id:
          type: string
        name:
          type: string
        onboarding_completed:
          type: boolean
        phone:
          type: string
        timezone:
          type: string
        updated_at:
          type: string
      type: object
    app.openapiPaginatedEnvelope:
      properties:
        cursor_applied:
          type: boolean
        has_more:
          type: boolean
        items:
          items:
            additionalProperties: true
            type: object
          type: array
        limit:
          type: integer
        next_cursor:
          type: string
        offset:
          type: integer
        order:
          type: string
        page:
          type: integer
        page_size:
          type: integer
        sort:
          type: string
        total:
          type: integer
        total_count:
          type: integer
      type: object
    app.openapiPlanningEventsListResponse:
      properties:
        cursor_applied:
          type: boolean
        events:
          items:
            additionalProperties: true
            type: object
          type: array
        from:
          type: string
        has_more:
          type: boolean
        instances:
          items:
            additionalProperties: true
            type: object
          type: array
        items:
          items:
            additionalProperties: true
            type: object
          type: array
        limit:
          type: integer
        next_cursor:
          type: string
        offset:
          type: integer
        order:
          type: string
        page:
          type: integer
        page_size:
          type: integer
        sort:
          type: string
        to:
          type: string
        total:
          type: integer
        total_count:
          type: integer
        warning:
          type: string
      type: object
    app.openapiPolicyReason:
      properties:
        details:
          additionalProperties: true
          type: object
        entity_id:
          type: string
        entity_type:
          type: string
        reason_code:
          type: string
        structured_code:
          $ref: '#/components/schemas/app.openapiStructuredReasonCode'
      type: object
    app.openapiRevokeAllEnvelope:
      properties:
        deleted:
          type: integer
        ok:
          type: boolean
      type: object
    app.openapiSessionRecord:
      properties:
        created_at:
          type: string
        email:
          type: string
        expires_at:
          type: string
        id:
          type: string
        name:
          type: string
        user_id:
          type: string
      type: object
    app.openapiSessionRevokeEnvelope:
      properties:
        id:
          type: string
        revoked:
          type: boolean
      type: object
    app.openapiSessionsEnvelope:
      properties:
        items:
          items:
            $ref: '#/components/schemas/app.openapiSessionRecord'
          type: array
        sessions:
          items:
            $ref: '#/components/schemas/app.openapiSessionRecord'
          type: array
        supports_revoke:
          type: boolean
        supports_revoke_all:
          type: boolean
        supportsRevoke:
          type: boolean
        supportsRevokeAll:
          type: boolean
      type: object
    app.openapiStructuredReasonCode:
      properties:
        code:
          type: string
        namespace:
          type: string
        rule_id:
          type: string
        rule_version:
          type: string
      type: object
    app.openapiStudentPortalAccess:
      properties:
        enabled:
          type: boolean
        organization_ids:
          items:
            type: string
          type: array
      type: object
    app.openapiValidationErrorResponse:
      properties:
        code:
          examples:
            - VALIDATION_ERROR
          type: string
        fields:
          items:
            $ref: '#/components/schemas/app.openapiFieldError'
          type: array
        message:
          examples:
            - validation failed
          type: string
        request_id:
          examples:
            - a1b2c3d4e5f6
          type: string
      type: object
    app.organizationPatchRequest:
      properties:
        address:
          type: string
        billing_email:
          type: string
        country:
          description: |-
            V1-B2: country + currency made first-class on the org profile.
            Both validated against ISO catalogues server-side; alternates
            in camelCase + snake_case accommodate the console's tolerant
            payload shape.
          type: string
        country_code:
          type: string
        currency:
          type: string
        currency_code:
          type: string
        name:
          type: string
        onboarding_completed:
          type: boolean
        onboardingCompleted:
          type: boolean
        org_type:
          type: string
        orgType:
          type: string
        phone:
          type: string
        timezone:
          type: string
      type: object
    app.packRevokeRequest:
      properties:
        reason:
          type: string
      type: object
    app.packSchoolReviewRequest:
      properties:
        clear:
          type: boolean
        notes:
          type: string
      type: object
    app.patchARCRequest:
      properties:
        arc_expires_at:
          description: YYYY-MM-DD or null/empty to clear
          type: string
        arc_last_renewed_at:
          description: YYYY-MM-DD or null/empty
          type: string
        arc_notes:
          type: string
        mark_renewed:
          description: |-
            Convenience flag: if true, sets arc_last_renewed_at = today and bumps
            arc_expires_at by 12 months when not provided in this request.
          type: boolean
      type: object
    app.patchComplianceDigestPrefRequest:
      properties:
        cadence:
          type: string
        locale:
          type: string
        weekday:
          type: integer
      type: object
    app.pauseDunningDTO:
      properties:
        days:
          description: |-
            Days is a shortcut: "pause for N days from now". Used when the
            UI exposes a "snooze 7 days" affordance.
          maximum: 365
          minimum: 1
          type: integer
        reason:
          maxLength: 500
          type: string
        until:
          description: Until is RFC3339 or YYYY-MM-DD. Past/empty value clears the pause.
          maxLength: 64
          type: string
      type: object
    app.paymentAllocationDTO:
      properties:
        invoice_id:
          type: string
      required:
        - invoice_id
      type: object
    app.paymentPatchDTO:
      properties:
        amount_cents:
          type: integer
        enrollment_id:
          type: string
        method:
          type: string
        notes:
          type: string
        program_id:
          type: string
        reference:
          type: string
        status:
          type: string
        student_id:
          type: string
        student_person_id:
          type: string
      type: object
    app.pilotBlockerAckResponse:
      properties:
        acknowledged_at:
          type: string
        acknowledged_text:
          type: string
        blocker_key:
          type: string
        compliance_record_id:
          type: string
        flight_id:
          type: string
        id:
          type: string
        organization_id:
          type: string
        user_id:
          type: string
      type: object
    app.pilotCareerEventResponse:
      properties:
        created_at:
          type: string
        event_type:
          type: string
        id:
          type: string
        metadata:
          type: object
        occurred_at:
          type: string
        organization_id:
          type: string
        organization_name:
          type: string
        pilot_profile_id:
          type: string
      type: object
    app.pilotDayBlocker:
      properties:
        acknowledged:
          type: boolean
        acknowledged_at:
          type: string
        blocker_key:
          type: string
        compliance_record_id:
          type: string
        detail:
          type: string
        expires_at:
          type: string
        organization_id:
          type: string
        organization_name:
          type: string
        severity:
          description: '"blocker" | "warning"'
          type: string
        title:
          type: string
      type: object
    app.pilotDayBundle:
      properties:
        blockers:
          items:
            $ref: '#/components/schemas/app.pilotDayBlocker'
          type: array
        etag:
          type: string
        generated_at:
          type: string
        inbox:
          items:
            $ref: '#/components/schemas/app.pilotDayInboxItem'
          type: array
        next_flight:
          $ref: '#/components/schemas/app.pilotDayFlight'
        revision:
          type: integer
        sections:
          additionalProperties:
            $ref: '#/components/schemas/app.pilotDaySection'
          type: object
        today_flights:
          items:
            $ref: '#/components/schemas/app.pilotDayFlight'
          type: array
        user_id:
          type: string
        valid_until:
          type: string
      type: object
    app.pilotDayFlight:
      properties:
        aircraft_registration:
          type: string
        arrival_icao:
          type: string
        briefing_available:
          type: boolean
        departure_icao:
          type: string
        ended_at:
          type: string
        flight_id:
          type: string
        has_fdm_data:
          type: boolean
        organization_id:
          type: string
        organization_name:
          type: string
        source:
          description: '"ops" | "training" | "planning"'
          type: string
        started_at:
          type: string
        status:
          type: string
      type: object
    app.pilotDayInboxItem:
      properties:
        counterpart_name:
          type: string
        last_message_at:
          type: string
        last_message_body:
          type: string
        subject:
          type: string
        thread_id:
          type: string
        unread_count:
          type: integer
      type: object
    app.pilotDaySection:
      properties:
        availability:
          type: string
        note:
          type: string
      type: object
    app.pilotOrgLinkResponse:
      properties:
        consent_status:
          type: string
        created_at:
          type: string
        id:
          type: string
        organization_id:
          type: string
        organization_name:
          type: string
        pilot_profile_id:
          type: string
        role_in_org:
          type: string
      type: object
    app.pilotProfileCareerEventRequest:
      properties:
        event_type:
          type: string
        metadata:
          type: object
        occurred_at:
          type: string
        organization_id:
          type: string
        pilot_profile_id:
          type: string
      type: object
    app.pilotProfileLinkOrgRequest:
      properties:
        action:
          type: string
        organization_id:
          type: string
        pilot_profile_id:
          type: string
        role_in_org:
          type: string
      type: object
    app.planningEventCreateRequest:
      properties:
        aircraft_id:
          maxLength: 64
          type: string
        aircraftId:
          maxLength: 64
          type: string
        conflict_override_reason:
          maxLength: 500
          type: string
        end_at:
          maxLength: 64
          type: string
        endAt:
          maxLength: 64
          type: string
        event_links:
          items:
            type: object
          type: array
        eventLinks:
          items:
            type: object
          type: array
        force:
          description: |-
            Planning Parity V1 — L2: explicit conflict override.
            Set Force=true together with a non-empty ConflictOverrideReason
            (>= 10 chars) when the caller has already seen the conflicts, has
            permission planning.override_conflict, and chose to book anyway.
          type: boolean
        instructor_id:
          maxLength: 64
          type: string
        instructorId:
          maxLength: 64
          type: string
        kind:
          maxLength: 64
          type: string
        links:
          items:
            type: object
          type: array
        notes:
          maxLength: 4000
          type: string
        recurrence: {}
        resource_id:
          maxLength: 64
          type: string
        resource_type:
          enum:
            - aircraft
            - instructor
            - student
            - room
            - custom
          type: string
        resourceId:
          maxLength: 64
          type: string
        resourceType:
          enum:
            - aircraft
            - instructor
            - student
            - room
            - custom
          type: string
        start_at:
          maxLength: 64
          type: string
        startAt:
          maxLength: 64
          type: string
        status:
          enum:
            - scheduled
            - confirmed
            - tentative
            - cancelled
          type: string
        student_id:
          maxLength: 64
          type: string
        student_ids:
          items:
            type: string
          maxItems: 64
          type: array
        studentId:
          maxLength: 64
          type: string
        studentIds:
          items:
            type: string
          maxItems: 64
          type: array
        title:
          maxLength: 255
          type: string
      required:
        - title
      type: object
    app.planningEventPatchRequest:
      properties:
        aircraft_id:
          maxLength: 64
          type: string
        aircraftId:
          maxLength: 64
          type: string
        end_at:
          maxLength: 64
          type: string
        endAt:
          maxLength: 64
          type: string
        event_links:
          items:
            type: object
          type: array
        eventLinks:
          items:
            type: object
          type: array
        instructor_id:
          maxLength: 64
          type: string
        instructorId:
          maxLength: 64
          type: string
        kind:
          maxLength: 64
          type: string
        links:
          items:
            type: object
          type: array
        notes:
          maxLength: 4000
          type: string
        resource_id:
          maxLength: 64
          type: string
        resource_type:
          enum:
            - aircraft
            - instructor
            - student
            - room
            - custom
          type: string
        resourceId:
          maxLength: 64
          type: string
        resourceType:
          enum:
            - aircraft
            - instructor
            - student
            - room
            - custom
          type: string
        start_at:
          maxLength: 64
          type: string
        startAt:
          maxLength: 64
          type: string
        status:
          enum:
            - scheduled
            - confirmed
            - tentative
            - cancelled
          type: string
        student_id:
          maxLength: 64
          type: string
        studentId:
          maxLength: 64
          type: string
        title:
          maxLength: 255
          type: string
      type: object
    app.planningResourceCreateRequest:
      properties:
        metadata:
          type: object
        name:
          type: string
        ref_id:
          type: string
        status:
          type: string
        type:
          type: string
      type: object
    app.portableCareerEventResponse:
      properties:
        detail:
          type: string
        id:
          type: string
        kind:
          type: string
        occurred_at:
          type: string
        organization_id:
          type: string
        organization_name:
          type: string
        source:
          type: string
        title:
          type: string
      type: object
    app.portableIdentityResponse:
      properties:
        active_orgs:
          items:
            type: string
          type: array
        alumni_of:
          items:
            type: string
          type: array
        career_stage:
          type: string
        display_name:
          type: string
        first_event_at:
          type: string
        global_user_id:
          type: string
        last_event_at:
          type: string
        org_links:
          items:
            $ref: '#/components/schemas/app.portableOrgLinkResponse'
          type: array
        pilot_profile_id:
          type: string
        timeline:
          items:
            $ref: '#/components/schemas/app.portableCareerEventResponse'
          type: array
        verified_milestones:
          type: integer
      type: object
    app.portableOrgLinkResponse:
      properties:
        consent_status:
          type: string
        ended_at:
          type: string
        organization_id:
          type: string
        organization_name:
          type: string
        relationship:
          type: string
        role_in_org:
          type: string
        started_at:
          type: string
      type: object
    app.programStageView:
      properties:
        approved_program_id:
          type: string
        created_at:
          type: string
        description:
          type: string
        hours_required:
          type: number
        id:
          type: string
        lessons_required:
          type: integer
        name:
          type: string
        organization_id:
          type: string
        stage_number:
          type: integer
      type: object
    app.readinessCheck:
      properties:
        error:
          type: string
        latency_ms:
          type: number
        status:
          type: string
      type: object
    app.readinessResponse:
      properties:
        checks:
          additionalProperties:
            $ref: '#/components/schemas/app.readinessCheck'
          type: object
        status:
          type: string
      type: object
    app.recordReminderDTO:
      properties:
        body:
          maxLength: 8000
          type: string
        channel:
          description: |-
            Channel drives both behaviour AND audit honesty. "email" =
            actually send via notifications.Service; "manual" = staff
            already contacted the payer offline; "skipped" = staff chose
            not to send and wants the skip recorded with a reason.
          enum:
            - email
            - manual
            - skipped
          type: string
        level:
          description: |-
            Level lets staff force a specific level if the default
            derivation is wrong. Otherwise the handler uses the suggested
            level from deriveNextAction.
          maximum: 4
          minimum: 1
          type: integer
        reason:
          maxLength: 500
          type: string
        recipient_email:
          description: |-
            RecipientEmail overrides the student's stored email. Useful
            when the invoice is B2B with a separate billing contact.
          maxLength: 255
          type: string
        subject:
          description: |-
            Subject/Body override the default reminder template when set.
            Empty = use the level-specific default.
          maxLength: 255
          type: string
      required:
        - channel
      type: object
    app.refreshRequest:
      properties:
        refresh_token:
          maxLength: 1024
          minLength: 1
          type: string
      type: object
    app.refundRequest:
      properties:
        amount_cents:
          minimum: 1
          type: integer
        reason:
          enum:
            - duplicate
            - fraudulent
            - requested_by_customer
          type: string
      type: object
    app.renewalCreateRequest:
      properties:
        evidence_document_id:
          type: string
        evidence_url:
          type: string
        expires_at:
          type: string
        issued_at:
          type: string
        notes:
          type: string
        reference:
          type: string
      type: object
    app.renewalRecordResponse:
      properties:
        compliance_item_id:
          type: string
        created_at:
          type: string
        evidence_document_id:
          type: string
        evidence_url:
          type: string
        expires_at:
          type: string
        id:
          type: string
        issued_at:
          type: string
        item_category:
          type: string
        item_code:
          type: string
        item_name:
          type: string
        notes:
          type: string
        person_id:
          type: string
        reference:
          type: string
        review_notes:
          type: string
        review_state:
          type: string
        reviewed_at:
          type: string
        reviewed_by_user_id:
          type: string
        status:
          type: string
        superseded_at:
          type: string
        superseded_by_id:
          type: string
        supersedes_id:
          type: string
        updated_at:
          type: string
      type: object
    app.renewalReviewRequest:
      properties:
        decision:
          type: string
        notes:
          type: string
      type: object
    app.roleCreateRequest:
      properties:
        code:
          type: string
        description:
          type: string
        name:
          type: string
        permission_ids:
          items:
            type: string
          type: array
        permissionIds:
          items:
            type: string
          type: array
        permissions:
          items:
            type: string
          type: array
      type: object
    app.rolePatchRequest:
      properties:
        code:
          type: string
        description:
          type: string
        id:
          type: string
        name:
          type: string
        permission_ids:
          items:
            type: string
          type: array
        permissionIds:
          items:
            type: string
          type: array
        permissions:
          items:
            type: string
          type: array
        role_id:
          type: string
        roleId:
          type: string
      type: object
    app.runAutomationNowRequest:
      properties:
        event_type:
          type: string
        job_type:
          type: string
        jobType:
          type: string
        payload:
          type: object
        rule_code:
          type: string
        rule_id:
          type: string
        ruleId:
          type: string
        run_id:
          type: string
        runId:
          type: string
      type: object
    app.scimEmailValue:
      properties:
        primary:
          type: boolean
        value:
          type: string
      type: object
    app.scimGroupRequest:
      properties:
        displayName:
          type: string
        externalId:
          type: string
      type: object
    app.scimNameValue:
      properties:
        familyName:
          type: string
        formatted:
          type: string
        givenName:
          type: string
      type: object
    app.scimPatchOperation:
      properties:
        op:
          type: string
        path:
          type: string
        value: {}
      type: object
    app.scimPatchRequest:
      properties:
        Operations:
          items:
            $ref: '#/components/schemas/app.scimPatchOperation'
          type: array
      type: object
    app.scimRoleValue:
      properties:
        display:
          type: string
        value:
          type: string
      type: object
    app.scimTokenCreateRequest:
      properties:
        expires_in_days:
          type: integer
        expiresInDays:
          type: integer
        name:
          type: string
        scopes:
          items:
            type: string
          type: array
      type: object
    app.scimUserRequest:
      properties:
        active:
          type: boolean
        emails:
          items:
            $ref: '#/components/schemas/app.scimEmailValue'
          type: array
        externalId:
          type: string
        name:
          $ref: '#/components/schemas/app.scimNameValue'
        roles:
          items:
            $ref: '#/components/schemas/app.scimRoleValue'
          type: array
        userName:
          type: string
      type: object
    app.sepaCollectionCreateDTO:
      properties:
        amount_cents:
          maximum: 1000000000
          minimum: 1
          type: integer
        currency:
          maxLength: 8
          type: string
        invoice_id:
          type: string
        mandate_id:
          type: string
        notes:
          maxLength: 4000
          type: string
      required:
        - amount_cents
        - mandate_id
      type: object
    app.sepaMandateCreateDTO:
      properties:
        account_holder_name:
          maxLength: 140
          type: string
        debtor_bic:
          maxLength: 11
          type: string
        debtor_email:
          maxLength: 255
          type: string
        debtor_iban:
          maxLength: 64
          type: string
        mandate_reference:
          maxLength: 35
          type: string
        notes:
          maxLength: 4000
          type: string
        scheme:
          enum:
            - core
            - b2b
          type: string
        signature_mode:
          enum:
            - paper
            - electronic_simple
            - electronic_qualified
          type: string
        signed_at:
          maxLength: 64
          type: string
        student_id:
          type: string
      required:
        - account_holder_name
        - debtor_iban
      type: object
    app.sessionRevokeRequest:
      properties:
        session_id:
          type: string
        sessionId:
          type: string
      type: object
    app.setupChecklistItem:
      properties:
        evidence_count:
          type: integer
        key:
          type: string
        label_key:
          type: string
        link:
          type: string
        required:
          type: boolean
        status:
          type: string
      type: object
    app.setupChecklistResponse:
      properties:
        items:
          items:
            $ref: '#/components/schemas/app.setupChecklistItem'
          type: array
        summary:
          $ref: '#/components/schemas/app.setupChecklistSummary'
      type: object
    app.setupChecklistSummary:
      properties:
        completed:
          type: integer
        optional_completed:
          type: integer
        optional_total:
          type: integer
        percent_complete:
          type: integer
        total:
          type: integer
      type: object
    app.sharedListingResponse:
      properties:
        aircraft_id:
          type: string
        aircraft_registration:
          type: string
        aircraft_type:
          type: string
        allow_external_pic:
          type: boolean
        base_icao:
          type: string
        created_at:
          type: string
        currency:
          type: string
        host_organization_id:
          type: string
        host_organization_name:
          type: string
        hourly_rate_cents:
          type: integer
        id:
          type: string
        ifr_capable:
          type: boolean
        notes:
          type: string
        status:
          type: string
        terms_summary:
          type: string
        updated_at:
          type: string
      type: object
    app.signoffRefView:
      properties:
        payload_hash:
          description: hex
          type: string
        signed_at:
          type: string
        signing_key_id:
          type: string
        signoff_id:
          type: string
      type: object
    app.signupOrgRequest:
      properties:
        email:
          maxLength: 320
          type: string
        full_name:
          maxLength: 200
          type: string
        fullName:
          maxLength: 200
          type: string
        organization_name:
          maxLength: 200
          type: string
        organizationName:
          maxLength: 200
          type: string
        password:
          maxLength: 512
          minLength: 8
          type: string
      required:
        - email
        - password
      type: object
    app.signupRequest:
      properties:
        email:
          maxLength: 320
          type: string
        full_name:
          maxLength: 200
          type: string
        password:
          maxLength: 512
          minLength: 8
          type: string
      required:
        - email
        - password
      type: object
    app.studentUpsertRequest:
      properties:
        email:
          type: string
        first_name:
          type: string
        instructor_id:
          type: string
        instructorId:
          type: string
        last_name:
          type: string
        metadata:
          type: object
        name:
          type: string
        notes:
          type: string
        primary_instructor_id:
          type: string
        primaryInstructorId:
          type: string
        program_id:
          type: string
        programId:
          type: string
        status:
          type: string
        user_id:
          type: string
        userId:
          type: string
      type: object
    app.syncDeviceRow:
      properties:
        change_count:
          type: integer
        client_type:
          type: string
        fingerprint:
          type: string
        first_seen_at:
          type: string
        health:
          type: string
        id:
          type: string
        last_ip:
          type: string
        last_seen_at:
          type: string
        name:
          type: string
        organization_id:
          type: string
        status:
          type: string
        user_agent:
          type: string
        user_id:
          type: string
      type: object
    app.tcoDocumentView:
      properties:
        approved_program_id:
          type: string
        document_url:
          type: string
        faa_approval_date:
          type: string
        faa_approval_letter_url:
          type: string
        id:
          type: string
        organization_id:
          type: string
        uploaded_at:
          type: string
        uploaded_by:
          type: string
        version:
          type: string
      type: object
    app.transitionBookingPayload:
      properties:
        decision_reason:
          type: string
        status:
          type: string
      type: object
    app.trialCheckoutRequest:
      properties:
        fleet_size:
          type: string
        full_name:
          type: string
        organization_name:
          type: string
        organization_type:
          type: string
        plan:
          type: string
        work_email:
          type: string
      type: object
    app.updateBaseRequest:
      properties:
        iata_code:
          maxLength: 10
          type: string
        icao_code:
          maxLength: 10
          type: string
        name:
          maxLength: 200
          type: string
        timezone:
          maxLength: 100
          type: string
      type: object
    app.upsertListingRequest:
      properties:
        aircraft_id:
          type: string
        allow_external_pic:
          type: boolean
        base_icao:
          type: string
        currency:
          type: string
        hourly_rate_cents:
          type: integer
        ifr_capable:
          type: boolean
        notes:
          type: string
        status:
          type: string
        terms_summary:
          type: string
      type: object
    intelligence.Anomaly:
      properties:
        baseline:
          description: |-
            Baseline is the reference value (e.g. 28d median) the current
            window is compared to.
          type: number
        baseline_window_days:
          description: BaselineWindowDays is the length of the baseline window in days.
          type: integer
        current:
          description: Current is the value measured on the current window.
          type: number
        description:
          type: string
        detected_at:
          type: string
        id:
          type: string
        metric:
          type: string
        ratio:
          description: |-
            Ratio is Current / Baseline (or absolute delta for count-like
            metrics). The UI renders this as "2.1× baseline" etc.
          type: number
        recommendation:
          description: |-
            Recommendation is the next concrete action. Must be a verb,
            must be something the school can do. Must not be empty.
          type: string
        rule:
          type: string
        severity:
          $ref: '#/components/schemas/intelligence.AnomalySeverity'
        title:
          type: string
        window_days:
          description: WindowDays is the length of the current window in days.
          type: integer
      type: object
    intelligence.AnomalyReport:
      properties:
        generated_at:
          type: string
        items:
          items:
            $ref: '#/components/schemas/intelligence.Anomaly'
          type: array
        missing_signals:
          items:
            type: string
          type: array
        organization_id:
          type: string
        rules_evaluated:
          description: |-
            RulesEvaluated is the total number of rules the engine tried
            to run (all rules, always).
          type: integer
        rules_insufficient_data:
          description: |-
            RulesInsufficientData is the number of rules that could not
            produce a verdict because the backing data was not available
            or too sparse.
          type: integer
      type: object
    intelligence.AnomalySeverity:
      enum:
        - critical
        - warning
        - info
      type: string
      x-enum-varnames:
        - SeverityCritical
        - SeverityWarning
        - SeverityInfo
    intelligence.CohortAnalytics:
      properties:
        cohorts:
          items:
            $ref: '#/components/schemas/intelligence.CohortSummary'
          type: array
        generated_at:
          type: string
        organization_id:
          type: string
        total_programs:
          description: |-
            TotalPrograms / TotalStudents are reported verbatim so the UI
            can render "24 students across 3 programs" without another
            round trip.
          type: integer
        total_students:
          type: integer
      type: object
    intelligence.CohortSummary:
      properties:
        active_enrollments:
          type: integer
        at_risk_count:
          type: integer
        avg_coverage_pct:
          type: number
        behind_count:
          type: integer
        health_flag:
          description: |-
            HealthFlag is one of: on_track | watch | behind | stalled.
            Derived from explicit rules — never a synthetic score.
          type: string
        on_track_count:
          type: integer
        program_id:
          type: string
        program_name:
          type: string
        stalled_count:
          type: integer
        student_count:
          type: integer
        template_version_lag:
          type: boolean
        velocity_pct_per_week:
          description: |-
            VelocityPctPerWeek is the average signoff-coverage delta over
            the last 4 weeks, expressed as a percentage-point delta per
            week. Nil when we lack enough historical snapshots to
            compute it honestly.
          type: number
      type: object
    intelligence.DimensionID:
      enum:
        - compliance
        - training
        - ops
        - finance
        - fleet
      type: string
      x-enum-varnames:
        - DimensionCompliance
        - DimensionTraining
        - DimensionOps
        - DimensionFinance
        - DimensionFleet
    intelligence.HealthCoverage:
      properties:
        dimensions_available:
          type: integer
        dimensions_total:
          type: integer
        missing_dimensions:
          items:
            type: string
          type: array
        weight_available:
          type: number
      type: object
    intelligence.HealthDimension:
      properties:
        base_weight:
          type: number
        contribution:
          description: |-
            Contribution is Score * NormalizedWeight, rounded to 1dp.
            Nil when Score is nil.
          type: number
        detail:
          description: |-
            Detail is a short, human-readable reason describing why the
            score landed where it did — or why it is missing.
          type: string
        id:
          $ref: '#/components/schemas/intelligence.DimensionID'
        label:
          type: string
        normalized_weight:
          description: |-
            NormalizedWeight is the weight after redistribution among
            available dimensions. Nil when Score is nil.
          type: number
        reason:
          description: |-
            Reason is a stable enum consumed by the UI when Score is nil.
            Empty string when Score is present.
          type: string
        score:
          type: number
      type: object
    intelligence.HealthIndex:
      properties:
        coverage:
          $ref: '#/components/schemas/intelligence.HealthCoverage'
        dimensions:
          items:
            $ref: '#/components/schemas/intelligence.HealthDimension'
          type: array
        generated_at:
          type: string
        organization_id:
          type: string
        partial:
          description: |-
            Partial is true when Score was computed with fewer than all
            dimensions. Callers should surface this prominently.
          type: boolean
        score:
          type: number
      type: object
    platform.AddonListing:
      properties:
        automated_check_results:
          items:
            type: integer
          type: array
        capabilities:
          items:
            type: string
          type: array
        category:
          type: string
        created_at:
          type: string
        description:
          type: string
        developer_app_id:
          type: string
        events_used:
          items:
            type: string
          type: array
        homepage_url:
          description: |-
            HomepageURL is sourced via the developer_app's homepage_url for the
            Task 4 automated checks (no dedicated addon_listings column in B2.1).
            Populated by callers that need it (currently: the Submit path's
            loadDeveloperAppForReview projection); empty otherwise.
          type: string
        id:
          type: string
        install_count:
          type: integer
        install_doc_url:
          type: string
        install_url:
          type: string
        listed_at:
          type: string
        required_scopes:
          items:
            type: string
          type: array
        review_notes:
          type: string
        review_status:
          $ref: '#/components/schemas/platform.ReviewStatus'
        reviewed_at:
          type: string
        reviewed_by_user_id:
          type: string
        scope_justifications:
          additionalProperties:
            type: string
          type: object
        slug:
          type: string
        status:
          $ref: '#/components/schemas/platform.ListingStatus'
        submitted_at:
          type: string
        summary:
          type: string
        support_email:
          description: |-
            SupportEmail mirrors HomepageURL — sourced via the developer_app for
            the manifest_required_fields check.
          type: string
        title:
          type: string
        updated_at:
          type: string
        vendor:
          type: string
        webhook_url_template:
          description: |-
            WebhookURLTemplate is an in-memory-only field at B2.1 (no DB column
            yet). The Task 4 webhook checks read it; a future migration will
            persist it on the addon_listings table. Empty string means "no
            webhook templated", which makes both webhook checks pass.
          type: string
      type: object
    platform.AppInstallation:
      properties:
        addon_listing_id:
          type: string
        audit_id:
          type: string
        consent_scopes:
          items:
            type: string
          type: array
        consented_at:
          type: string
        consented_by_user_id:
          type: string
        disabled_at:
          type: string
        id:
          type: string
        installed_at:
          type: string
        installed_by_user_id:
          type: string
        linked_api_key_id:
          type: string
        linked_webhook_endpoint_id:
          type: string
        organization_id:
          type: string
        settings:
          items:
            type: integer
          type: array
        status:
          $ref: '#/components/schemas/platform.InstallStatus'
        uninstalled_at:
          type: string
        updated_at:
          type: string
      type: object
    platform.AppStatus:
      enum:
        - draft
        - active
        - revoked
      type: string
      x-enum-varnames:
        - AppStatusDraft
        - AppStatusActive
        - AppStatusRevoked
    platform.AppType:
      enum:
        - private
        - partner
        - internal
      type: string
      x-enum-varnames:
        - AppTypePrivate
        - AppTypePartner
        - AppTypeInternal
    platform.ConfirmResult:
      properties:
        api_key_plaintext:
          type: string
        idempotent:
          type: boolean
        install_url:
          type: string
        installation_id:
          type: string
        webhook_secret_plaintext:
          type: string
      type: object
    platform.ConsentPayload:
      properties:
        api_key_name:
          type: string
        audit_id:
          type: string
        install_url:
          type: string
        listing_id:
          type: string
        listing_slug:
          type: string
        listing_title:
          type: string
        provider_homepage_url:
          type: string
        provider_name:
          type: string
        scopes_to_grant:
          items:
            $ref: '#/components/schemas/platform.ConsentScopeRow'
          type: array
        webhook_event_types:
          items:
            type: string
          type: array
        webhook_url_template:
          type: string
      type: object
    platform.ConsentScopeRow:
      properties:
        justification:
          type: string
        scope:
          type: string
      type: object
    platform.DeveloperApp:
      properties:
        app_type:
          $ref: '#/components/schemas/platform.AppType'
        created_at:
          type: string
        created_by_user_id:
          type: string
        declared_scopes:
          items:
            type: string
          type: array
        description:
          type: string
        homepage_url:
          type: string
        id:
          type: string
        last_used_at:
          type: string
        name:
          type: string
        owner_org_id:
          type: string
        redirect_uris:
          items:
            type: string
          type: array
        revocation_reason:
          type: string
        revoked_at:
          type: string
        slug:
          type: string
        status:
          $ref: '#/components/schemas/platform.AppStatus'
        support_email:
          type: string
        updated_at:
          type: string
      type: object
    platform.InstallStatus:
      enum:
        - installed
        - disabled
        - uninstalled
      type: string
      x-enum-varnames:
        - InstallStatusInstalled
        - InstallStatusDisabled
        - InstallStatusUninstalled
    platform.InstallationRow:
      properties:
        addon_listing_id:
          type: string
        consent_scopes:
          items:
            type: string
          type: array
        consented_at:
          type: string
        consented_by_user_id:
          type: string
        disabled_at:
          type: string
        id:
          type: string
        installed_at:
          type: string
        installed_by_user_id:
          type: string
        linked_api_key_id:
          type: string
        linked_webhook_endpoint_id:
          type: string
        listing_name:
          type: string
        listing_slug:
          type: string
        organization_id:
          type: string
        provider_name:
          type: string
        status:
          $ref: '#/components/schemas/platform.InstallStatus'
        uninstalled_at:
          type: string
        updated_at:
          type: string
      type: object
    platform.ListingStatus:
      enum:
        - private
        - review
        - public
        - deprecated
      type: string
      x-enum-varnames:
        - ListingStatusPrivate
        - ListingStatusReview
        - ListingStatusPublic
        - ListingStatusDeprecated
    platform.PublicListingRow:
      properties:
        category:
          type: string
        created_at:
          type: string
        description:
          type: string
        id:
          type: string
        install_count:
          type: integer
        install_doc_url:
          type: string
        install_url:
          type: string
        listed_at:
          type: string
        provider_name:
          type: string
        reviewed_at:
          type: string
        scopes_required:
          items:
            type: string
          type: array
        slug:
          type: string
        subscribed_event_types:
          items:
            type: string
          type: array
        summary:
          type: string
        title:
          type: string
        updated_at:
          type: string
        vendor:
          type: string
        webhook_url_template:
          type: string
      type: object
    platform.ReviewStatus:
      enum:
        - not_submitted
        - automated_checks_failed
        - pending_review
        - approved
        - rejected
      type: string
      x-enum-varnames:
        - ReviewStatusNotSubmitted
        - ReviewStatusAutomatedChecksFailed
        - ReviewStatusPendingReview
        - ReviewStatusApproved
        - ReviewStatusRejected
    platform.RotateAPIKeyResult:
      properties:
        api_key_plaintext:
          type: string
        installation_id:
          type: string
        new_api_key_id:
          type: string
        new_api_key_plaintext:
          type: string
        old_api_key_id:
          type: string
        rotated_at:
          type: string
      type: object
    platform.RotateInstallWebhookSecretResult:
      properties:
        installation_id:
          type: string
        rotated_at:
          type: string
        secondary_expires_at:
          type: string
        webhook_endpoint_id:
          type: string
        webhook_secret_plaintext:
          type: string
      type: object
    platform.RotationResult:
      properties:
        new_secret_plaintext:
          type: string
        secondary_expires_at:
          type: string
      type: object
    platform.UsageSummary:
      properties:
        active_keys:
          type: integer
        install_count:
          description: 0 in B1 (computed in B3)
          type: integer
        last_used_at:
          type: string
        revoked_keys:
          type: integer
      type: object
    training.HeatmapCellStatus:
      enum:
        - signed
        - pending
        - unsigned
        - not_required
      type: string
      x-enum-varnames:
        - HeatmapCellSigned
        - HeatmapCellPending
        - HeatmapCellUnsigned
        - HeatmapCellNotRequired
    training.HeatmapStudent:
      properties:
        coverage:
          additionalProperties:
            $ref: '#/components/schemas/training.HeatmapCellStatus'
          type: object
        coverage_pct:
          type: number
        enrollment_days:
          type: integer
        enrollment_id:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        pending_count:
          type: integer
        signed_count:
          type: integer
        student_id:
          type: string
        unsigned_count:
          type: integer
      type: object
    training.ProgramAtRiskList:
      properties:
        generated_at:
          type: string
        items:
          items:
            $ref: '#/components/schemas/training.ProgramAtRiskStudent'
          type: array
        program_id:
          type: string
        program_name:
          type: string
        total:
          type: integer
      type: object
    training.ProgramAtRiskStudent:
      properties:
        coverage_pct:
          type: number
        days_since_flight:
          type: integer
        enrollment_days:
          type: integer
        enrollment_id:
          type: string
        first_name:
          type: string
        last_flight_date:
          type: string
        last_name:
          type: string
        reasons:
          items:
            type: string
          type: array
        signed_count:
          type: integer
        student_id:
          type: string
        unsigned_required:
          type: integer
      type: object
    training.ProgramHeatmap:
      properties:
        generated_at:
          type: string
        items:
          items:
            $ref: '#/components/schemas/training.ProgramItemSummary'
          type: array
        program_id:
          type: string
        program_name:
          type: string
        students:
          items:
            $ref: '#/components/schemas/training.HeatmapStudent'
          type: array
        top_blocking_items:
          items:
            $ref: '#/components/schemas/training.ProgramItemSummary'
          type: array
      type: object
    training.ProgramItemSummary:
      properties:
        code:
          type: string
        id:
          type: string
        kind:
          type: string
        pending_count:
          type: integer
        signed_count:
          type: integer
        signoff_required:
          type: boolean
        sort_order:
          type: integer
        title:
          type: string
        unsigned_count:
          type: integer
      type: object
    training.ProgramProgressSummary:
      properties:
        active_enrollments:
          type: integer
        at_risk_count:
          type: integer
        avg_coverage_pct:
          type: number
        behind_count:
          type: integer
        generated_at:
          type: string
        latest_template_version:
          type: integer
        on_track_count:
          type: integer
        program_id:
          type: string
        program_name:
          type: string
        signoff_required_items:
          type: integer
        student_count:
          type: integer
        template_version:
          type: integer
        template_version_lag:
          type: boolean
        total_items:
          type: integer
      type: object
