# Proposed contract, version 1: endpoints, errors, paging, idempotency, security and the capability of every
# operation. Resource schemas were initialized from the data model (docs/06-data) and are kept aligned with it; input
# schemas (*Input, *Request) and views are specific to this contract. Status: proposal for review, not confirmed design.
# Conventions: docs/05-interfaces/choosing-an-interface.md. Vendor extensions: x-moonitoring-audience (who calls),
# x-moonitoring-capability (the capability checked), x-moonitoring-entitlement (the entitlement feature checked),
# x-moonitoring-requirements (the requirements of 1.requisiti-raffinati.txt the operation serves).
openapi: 3.1.0
info:
  title: Releases Installations API
  version: 1.0.0
  description: 'Installed clients: enrolment, the update check that evaluates the channel rule again every time,
    the download of manifests and chunks through signed URLs, the events of every download and update, and the installation
    token. Staff read installations and events; clients act on their own.'
servers:
- url: https://releases.moonitoring.com/installations/v1
  description: Prod
- url: https://stage-releases.moonitoring.com/installations/v1
  description: Stage, through IAP
security:
- bearer: []
tags:
- name: Enrolment
  description: A new installation
- name: Updates
  description: Checks and downloads
- name: Events
  description: Downloads and updates
- name: Testers
  description: Proving a tester e-mail
paths:
  /installations:
    post:
      summary: Enrol an installation
      operationId: enrolInstallation
      description: 'The channel rule is evaluated for the caller. Publishes `installation.enrolled.v1`. For an application
        of the Organization audience, such as the farm sync client, the installer enrols with the token of the person
        who signed in to it in the browser (native app `moonitoring-installer`), active in the Organization they chose;
        the installation records that Organization and person.'
      tags:
      - Enrolment
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EnrolmentRequest'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstallationEnrolled'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      security:
      - {}
      - bearer: []
      - installationToken: []
      x-moonitoring-audience: client
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '118'
      - '118.1'
      - '118.8'
      - '118.11'
      - '118.12'
      - '111'
    get:
      summary: List installations
      operationId: listInstallations
      tags:
      - Updates
      parameters:
      - name: application
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Only this application's.
      - name: channel
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Only this channel's.
      - name: organization
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Only this Organization's.
      - name: current_build
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Only on this build.
      - name: seen_since
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Seen at or after.
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseInstallation'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.installations.read
      x-moonitoring-requirements:
      - '118'
      - '1006.6'
  /installations/summary:
    get:
      summary: Count an application's installations
      operationId: getInstallationSummary
      description: Decided on 2026-11-11. How many installations of one application are in use, per channel and per
        build, for the Console. Retired installations are not counted.
      tags:
      - Updates
      parameters:
      - name: application
        in: query
        required: true
        schema:
          type: string
          format: uuid
        description: The ReleaseApplication.
      responses:
        '200':
          description: The counts.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstallationSummary'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.installations.read
      x-moonitoring-requirements:
      - '118'
      - '1006.6'
  /installations/{installation_id}/update-check:
    post:
      summary: Check for an update
      operationId: checkForUpdate
      description: 'Bearer: the installation token. The rule is evaluated again with live memberships; `last_seen_at`
        is updated. Without `current_build` nothing is installed yet: the answer is the build the channel''s current
        TUF targets name, the active release, whatever the installation''s rollout bucket, since a superseded build
        is not in the current targets and the client could not verify it. It comes with `update_available` true,
        `mandatory` false, full manifests and no patch; `update_available` is false when the channel has no active
        release. The rollout percentage applies only to updates of an installed build. A retired installation answers
        401.'
      tags:
      - Updates
      parameters:
      - name: installation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCheckRequest'
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateCheckResult'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      security:
      - installationToken: []
      x-moonitoring-audience: client
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '118'
      - '118.6'
      - '111'
      - '112.4'
      - '112.5'
      - '106'
      - '108'
  /installations/{installation_id}/rotate-token:
    post:
      summary: Rotate the installation token
      operationId: rotateInstallationToken
      tags:
      - Enrolment
      parameters:
      - name: installation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstallationEnrolled'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
      security:
      - installationToken: []
      x-moonitoring-audience: client
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '118.12'
  /installations/{installation_id}/update-events:
    post:
      summary: Report an update
      operationId: createUpdateEvent
      description: 'Bearer: the installation token. A success is reported once the new version confirmed its start;
        the first success sets the installation''s `started_at`.'
      tags:
      - Events
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      - name: installation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseUpdateEventInput'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseUpdateEvent'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      security:
      - installationToken: []
      x-moonitoring-audience: client
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '119'
      - '119.5'
      - '119.7'
    get:
      summary: List an installation's events
      operationId: listInstallationUpdateEvents
      tags:
      - Events
      parameters:
      - name: installation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseUpdateEvent'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.installations.read
      x-moonitoring-requirements:
      - '119'
      - '1006.6'
  /update-events:
    post:
      summary: Report a download from the download page
      operationId: createDownloadEvent
      description: For the first download of an installer, before any enrolment; the signed-in person is recorded
        for private software.
      tags:
      - Events
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseUpdateEventInput'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseUpdateEvent'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      security:
      - {}
      - bearer: []
      x-moonitoring-audience: any
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '119'
      - '119.2'
      - '119.10'
    get:
      summary: List update events
      operationId: listUpdateEvents
      tags:
      - Events
      parameters:
      - name: to_build
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Only updates to this build.
      - name: outcome
        in: query
        required: false
        schema:
          type: string
          enum:
          - success
          - failed
          - rolled_back
        description: Only this outcome.
      - name: from
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Occurred at or after.
      - name: to
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Occurred before.
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseUpdateEvent'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.installations.read
      x-moonitoring-requirements:
      - '119'
      - '1006.6'
  /installations/{installation_id}:
    get:
      summary: Read an installation
      operationId: getInstallation
      description: A client reads its own; staff read any.
      tags:
      - Updates
      parameters:
      - name: installation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseInstallation'
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
      x-moonitoring-audience: any
      x-moonitoring-capability:
      - null
      - releases.installations.read
      x-moonitoring-requirements:
      - '118'
      - '1006.6'
  /tester-verifications:
    post:
      summary: Start a tester verification
      operationId: createTesterVerification
      tags:
      - Testers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TesterVerificationRequest'
      responses:
        '202':
          description: The result.
          content:
            application/json:
              schema:
                type: object
                description: The request was accepted; the work continues in the background and can be followed
                  by its id.
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The resource or run that was created to follow the work.
                  status:
                    type: string
                required:
                - id
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
        '429':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/TooManyRequests
      security: []
      x-moonitoring-audience: public
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '131'
      - '131.6'
  /tester-verifications/{verification_id}/confirm:
    post:
      summary: Confirm a tester verification
      operationId: confirmTesterVerification
      tags:
      - Testers
      parameters:
      - name: verification_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TesterVerificationConfirm'
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TesterProof'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
        '429':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/TooManyRequests
      security: []
      x-moonitoring-audience: public
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '131'
      - '131.6'
components:
  schemas:
    InstallationSummary:
      type: object
      description: The installations of one application that are not retired, counted per channel and per build
        (decided 2026-11-11).
      properties:
        application:
          type: string
          format: uuid
          description: ID of the ReleaseApplication.
        total:
          type: integer
        by_channel:
          type: array
          items:
            type: object
            properties:
              channel:
                type: string
                format: uuid
                description: ID of the ReleaseChannel.
              installations:
                type: integer
            required:
            - channel
            - installations
          description: Every channel with at least one installation, most installations first.
        by_build:
          type: array
          items:
            type: object
            properties:
              build:
                type: string
                format: uuid
                description: ID of the ReleaseBuild, the installation's current build.
              installations:
                type: integer
            required:
            - build
            - installations
          description: Every build at least one installation runs, most installations first.
      required:
      - application
      - total
      - by_channel
      - by_build
    ReleaseInstallation:
      description: One installed copy of one of our applications, on a given platform and channel. It records the
        build it runs and when it last contacted us to check for updates, which is what updates, rollouts and rollbacks
        work from. It also records who enrolled it (an Organization and user, a staff member, or a tester) and the
        hash of its token, so the channel's access rule can be checked again at every update with live memberships.
        For the farm sync client, the farm and its data synchronisation are kept separately in SyncClient.
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Key. Random, generated by the client; never derived from the hardware.
        application:
          type: string
          format: uuid
          description: ID of the ReleaseApplication.
        channel:
          type: string
          format: uuid
          description: ID of the ReleaseChannel.
        platform:
          type: string
          format: uuid
          description: ID of the ReleasePlatform.
        current_build:
          type: string
          format: uuid
          description: ID of the ReleaseBuild.
        rollout_bucket:
          type: integer
          description: 'Hash(id) % 100: with `rollout_pct` = 20 the buckets `0..19` update, always the same ones.'
        last_seen_at:
          type: string
          format: date-time
          description: Last time the client contacted us, to check for updates. Not a data synchronisation.
        organization:
          type: string
          format: uuid
          description: ID of the Organization. The Organization active at enrolment; set for the organization audience.
        user:
          type: string
          format: uuid
          description: ID of the User. Who enrolled it; set for the organization audience.
        moonitoring_user:
          type: string
          format: uuid
          description: ID of the MoonitoringUser. Who enrolled it; set for the moonitoring audience.
        tester:
          type: string
          format: uuid
          description: ID of the ReleaseTester. Set when the enrolment passed as a tester rather than through the
            audience rule.
        credential_hash:
          type: string
          description: 'Hash of the installation token the client presents on every update check: random, issued
            at enrolment, bound to this id, rotated when the enrolling identity changes. The token itself is never
            stored.'
        started_at:
          type: string
          format: date-time
          description: When the client first confirmed that it started, with a successful update event (req 118.13).
            Absent while it never started.
        retired_at:
          type: string
          format: date-time
          description: When releases retired it (req 118.14); its token is then refused. An installation that never
            confirms a start is retired 30 days after it enrolled.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
      - application
      - channel
      - platform
      - current_build
      - rollout_bucket
      - last_seen_at
      - credential_hash
      - created_at
      - updated_at
    ReleaseUpdateEvent:
      description: 'One download or update: an installation moving from one build to another, or a signed-in person
        downloading an installer from the download page before any enrolment exists. For private software this is
        the record of who downloaded what.'
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Key.
        installation:
          type: string
          format: uuid
          description: ID of the ReleaseInstallation. Empty for the download of an installer from the download page,
            before any enrolment.
        from_build:
          type: string
          format: uuid
          description: ID of the ReleaseBuild. Empty on the first installation.
        to_build:
          type: string
          format: uuid
          description: ID of the ReleaseBuild.
        method:
          type: string
          enum:
          - chunks
          - patch
          - full
        bytes_downloaded:
          type: integer
        outcome:
          type: string
          enum:
          - success
          - failed
          - rolled_back
        error_code:
          type: string
        occurred_at:
          type: string
          format: date-time
        user:
          type: string
          format: uuid
          description: ID of the User. Who downloaded, for the organization audience; the record that matters for
            private software.
        moonitoring_user:
          type: string
          format: uuid
          description: ID of the MoonitoringUser. Who downloaded, for the moonitoring audience.
        tester:
          type: string
          format: uuid
          description: ID of the ReleaseTester. Set when the download passed as a tester.
        platform:
          type: string
          format: uuid
          description: 'ID of the ReleasePlatform of an installer downloaded through a download link (req 119.13). Such
            an event is anonymous, for every audience: no installation, user or tester.'
      required:
      - id
      - to_build
      - method
      - bytes_downloaded
      - outcome
      - occurred_at
    EnrolmentRequest:
      type: object
      description: Enrols an installation on a channel. The caller is the audience's identity (an Organization user
        token, a staff token, or nothing for a public application), or a tester with a proof.
      properties:
        id:
          type: string
          format: uuid
          description: Random, generated by the client; never derived from the hardware.
        application:
          type: string
          format: uuid
        channel:
          type: string
          format: uuid
        platform:
          type: string
          format: uuid
        current_build:
          type: string
          format: uuid
          description: The build the installer carried.
        tester_proof:
          type: string
      required:
      - id
      - application
      - channel
      - platform
    InstallationEnrolled:
      type: object
      description: The installation and its token, shown once.
      properties:
        installation:
          $ref: '#/components/schemas/ReleaseInstallation'
        installation_token:
          type: string
          description: Presented as the bearer on every update check; stored only as a hash.
      required:
      - installation
      - installation_token
    UpdateCheckRequest:
      type: object
      description: What the client runs now.
      properties:
        current_build:
          type: string
          format: uuid
          description: The build installed now. Absent when nothing is installed yet, as for the installer before
            its first download; the nil UUID is not a build.
        installed_manifests:
          type: array
          items:
            type: object
            properties:
              depot:
                type: string
                format: uuid
              manifest:
                type: string
                format: uuid
            required:
            - depot
            - manifest
    UpdateCheckResult:
      type: object
      description: Whether there is an update for this installation, and what to fetch. An installation that no
        longer passes the channel rule, or is outside the rollout, receives no manifest and keeps what it has.
      properties:
        update_available:
          type: boolean
        mandatory:
          type: boolean
          description: current_build is below min_build_number.
        release:
          type: string
          format: uuid
          description: ID of the Release (Channels API).
        rollout_pct:
          type: integer
        notes:
          type: string
          description: Release notes, Markdown.
        build:
          type: string
          format: uuid
        version_label:
          type: string
        depots:
          type: array
          items:
            type: object
            properties:
              depot:
                type: string
                format: uuid
              manifest:
                type: string
                format: uuid
              manifest_download:
                $ref: ../../../../common/v1/schemas.yaml#/components/schemas/SignedUrl
              patch:
                type: string
                format: uuid
              patch_download:
                $ref: ../../../../common/v1/schemas.yaml#/components/schemas/SignedUrl
            required:
            - depot
            - manifest
            - manifest_download
        chunk_base_url:
          type: string
          description: Signed base for chunk downloads by hash on the CDN, valid for the check's lifetime. Set
            whenever an update is available, in stage as in prod (decided 2026-11-11).
          format: uri
        metadata_version:
          type: integer
          description: The targets metadata version this answer corresponds to.
      required:
      - update_available
    ReleaseUpdateEventInput:
      type: object
      description: One download or update, reported by the installation; the download page reports the first download
        of an installer without an installation.
      properties:
        from_build:
          type: string
          format: uuid
          description: ID of the ReleaseBuild. Empty on the first installation.
        to_build:
          type: string
          format: uuid
          description: ID of the ReleaseBuild.
        method:
          type: string
          enum:
          - chunks
          - patch
          - full
        bytes_downloaded:
          type: integer
        outcome:
          type: string
          enum:
          - success
          - failed
          - rolled_back
        error_code:
          type: string
        occurred_at:
          type: string
          format: date-time
      required:
      - to_build
      - method
      - bytes_downloaded
      - outcome
      - occurred_at
      additionalProperties: false
    TesterVerificationRequest:
      type: object
      description: 'Starts proving an e-mail: a one-time code is sent to it.'
      properties:
        email:
          type: string
          format: email
      required:
      - email
    TesterVerificationConfirm:
      type: object
      description: Confirms the code.
      properties:
        code:
          type: string
      required:
      - code
    TesterProof:
      type: object
      description: A short-lived proof the download page, enrolment and update checks accept in the `Moonitoring-Tester-Proof`
        header.
      properties:
        proof:
          type: string
        expires_at:
          type: string
          format: date-time
      required:
      - proof
      - expires_at
  securitySchemes:
    installationToken:
      type: http
      scheme: bearer
      description: 'The token of one installation, `mri_<random>`, issued at enrolment and rotated by the client; told
        apart from a token of `access` by its prefix (decided 2026-11-05). Only its SHA-256 is stored.'
    bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'The short-lived token signed by `access`: an Organization user token, a staff token or a client
        token, as the operation''s audience says.'
