"/eye-tracking/model":
    get:
        tags: [EyeTracking]
        operationId: getEyeTrackingModel
        summary: Eye-tracking model file (302 redirect)
        description: |
            **Returns a `302` redirect** to a presigned S3 URL pointing
            to the active eye-tracking model. Standard `apiKey` /
            `accessToken` middleware does **not** run on `/eye-tracking/*` —
            this route is gated solely by the per-headset JWT.

            The `model_s3_uri` query string parameter is required.
        security:
            - eyeTrackingToken: []
        parameters:
            - in: query
              name: model_s3_uri
              required: true
              schema: { type: string, format: uri }
              description: S3 URI the headset wants a presigned URL for.
        responses:
            "302":
                description: Redirect to the presigned S3 URL.
                headers:
                    Location:
                        description: Presigned S3 URL valid for a short window.
                        schema: { type: string, format: uri }
            "400":
                description: Missing `model_s3_uri` query parameter.
            "401":
                description: Missing or invalid `eyeTrackingToken`.

"/eye-tracking/user-files":
    get:
        tags: [EyeTracking]
        operationId: getEyeTrackingUserFiles
        summary: Per-user eye-tracking calibration file keys
        description: |
            Returns the **list of S3 keys** for calibration / personalisation
            files associated with the headset's `eyeTrackingToken`. The
            client fetches the actual files via separate presigned-URL
            calls.

            Supports `If-None-Match` — returns `304` when the supplied
            ETag still matches.
        security:
            - eyeTrackingToken: []
        parameters:
            - in: header
              name: If-None-Match
              required: false
              schema: { type: string }
        responses:
            "200":
                description: File-key list.
                headers:
                    ETag:
                        description: Hash of the file-key list — usable on the next request.
                        schema: { type: string }
                    Cache-Control:
                        schema: { type: string }
                content:
                    application/json:
                        schema:
                            type: array
                            items: { type: string, description: S3 key. }
            "304":
                description: ETag matches — no body.
            "401":
                description: Missing or invalid `eyeTrackingToken`.

"/eye-tracking/user-id":
    get:
        tags: [EyeTracking]
        operationId: getEyeTrackingUserId
        summary: User id linked to the eye-tracking token
        description: |
            Returns the **bare user-id string** (the JWT's `u` claim)
            that the `eyeTrackingToken` is currently bound to. Used by
            the headset to confirm pairing.
        security:
            - eyeTrackingToken: []
        responses:
            "200":
                description: User identifier (raw JSON string, not an object).
                content:
                    application/json:
                        schema:
                            type: string
                            description: Bare user-id string.
            "401":
                description: Missing or invalid `eyeTrackingToken`.
