"/token/purchase/{bigMediaId}":
    parameters:
        - in: path
          name: bigMediaId
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: sendPurchaseTokenByEmailLegacy
        summary: Email a purchase magic-link (legacy path)
        description: |
            Emails the caller a magic-link they can use to purchase the
            specified media product. Identical handler as the
            `/media/token/purchase/{bigMediaId}` route — kept at the
            top level for legacy clients.
        responses:
            "200":
                description: Email queued.
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/category/{name}":
    parameters:
        - in: path
          name: name
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: getPublicCategory
        summary: One media category (auth optional)
        description: |
            Returns a public `MediaCategory` by name. Access token is
            optional — when present, the response may include
            personalised flags (entitlement gating, ordering).
        security:
            - apiKey: []
        responses:
            "200":
                description: Category.
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/MediaCategory.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/categories/{name}/{after}":
    parameters:
        - in: path
          name: name
          required: true
          schema: { type: string }
        - in: path
          name: after
          required: true
          schema: { type: string }
          description: |
              ISO date or cursor string. Optional in the underlying route
              (`/media/categories/:name{/:after}`) — when omitted, returns
              the unpaginated list.
    get:
        tags: [Media]
        operationId: getPublicCategoryCollections
        summary: Category collections after a cursor (auth optional)
        description: |
            Returns the `ProductCollection`s in a category, optionally
            cursored at `after`. Access token is optional.
        security:
            - apiKey: []
        responses:
            "200":
                description: Collections.
                content:
                    application/json:
                        schema:
                            type: array
                            items: { $ref: "../components/schemas/ProductCollection.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/categories_as_events/{name}":
    parameters:
        - in: path
          name: name
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: getPublicCategoryCollectionsAsEvents
        summary: Category collections as events (auth optional)
        description: |
            HACK alternative shape — returns the current and future
            collections in the category formatted as event records (start /
            end times, titles). Used by the website's "What's on" widget.
        security:
            - apiKey: []
        responses:
            "200":
                description: Event-shaped collections.
                content:
                    application/json:
                        schema:
                            type: array
                            items:
                                type: object
                                additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/product/{bigMediaId}":
    parameters:
        - in: path
          name: bigMediaId
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: getPublicMediaProduct
        summary: One media product (auth optional)
        description: |
            Returns the public-facing `MediaProduct` by id. Access token
            is optional — when present, may include personalised price /
            availability for the caller's region.
        security:
            - apiKey: []
        responses:
            "200":
                description: Media product.
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/MediaProduct.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/location":
    get:
        tags: [Media]
        operationId: getPublicMediaLocation
        summary: Media-pricing location (auth optional)
        description: |
            Returns the location info the server uses for media pricing
            and playback decisions (region code, currency, rights region).
            Access token is optional.
        security:
            - apiKey: []
        responses:
            "200":
                description: Location payload.
                content:
                    application/json:
                        schema:
                            type: object
                            properties:
                                region: { type: string }
                                country: { type: string }
                                currency: { type: string }
                                rightsRegion: { type: string }
                            additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/entitlements/{bigMediaId}":
    parameters:
        - in: path
          name: bigMediaId
          required: true
          schema: { type: string }
          description: |
              Media product to scope to. Optional in the underlying route
              (`/media/entitlements{/:bigMediaId}`) — when omitted, returns
              every entitlement on the caller's account.
    get:
        tags: [Media]
        operationId: getPublicEntitlements
        summary: Caller's entitlements (optionally scoped)
        description: |
            Returns the caller's `Entitlement`s — optionally filtered to
            a specific `bigMediaId`. Used by the VR app to decide
            playback / order paths.
        responses:
            "200":
                description: Entitlements.
                content:
                    application/json:
                        schema:
                            type: array
                            items: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/checksum":
    get:
        tags: [Media]
        operationId: getEntitlementsChecksum
        summary: Entitlements checksum
        description: |
            Returns a hash of the caller's entitlement set. The VR client
            polls this and only refetches the full entitlements list when
            the hash changes — keeps refresh traffic down.
        responses:
            "200":
                description: Checksum payload.
                content:
                    application/json:
                        schema:
                            type: object
                            required: [checksum]
                            properties:
                                checksum: { type: string }
                                computedAt: { type: integer, description: Unix milliseconds. }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/check/{bigMediaId}":
    parameters:
        - in: path
          name: bigMediaId
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: checkMediaEntitlement
        summary: Caller's entitlement on one product
        description: |
            Returns the caller's `Entitlement`(s) on a specific media
            product — the same handler as `/media/entitlements/{id}`,
            scoped down.
        responses:
            "200":
                description: Entitlement(s).
                content:
                    application/json:
                        schema:
                            type: array
                            items: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/play/{bigMediaId}":
    parameters:
        - in: path
          name: bigMediaId
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: playMedia
        summary: Playback manifest for a media product
        description: |
            Returns the playback manifest (DRM URLs, audio tracks,
            subtitles) for a media product. Requires a valid entitlement;
            returns `403` otherwise.
        responses:
            "200":
                description: Manifest.
                content:
                    application/json:
                        schema:
                            type: object
                            description: |
                                Manifest shape varies by product format
                                (HLS, DASH, MP4) — keys include `url`,
                                `audioTracks`, `subtitleTracks`, `drm`.
                            additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "403":
                description: No entitlement for this product.
                content:
                    application/json:
                        schema: { $ref: "../public-api.yaml#/components/schemas/Error" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/start/{mediaRequest}":
    parameters:
        - in: path
          name: mediaRequest
          required: true
          schema: { type: string }
          description: |
              Comma-or-`|`-separated query string of fallback identifiers
              the server resolves in order, returning the first hit.
    get:
        tags: [Media]
        operationId: startMedia
        summary: Resolve and start a media stream
        description: |
            Walks the supplied fallback list and returns playback details
            (URL + metadata) for the first product that resolves.
        responses:
            "200":
                description: Playback descriptor.
                content:
                    application/json:
                        schema:
                            type: object
                            additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/token/purchase/{bigMediaId}":
    parameters:
        - in: path
          name: bigMediaId
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: sendPurchaseTokenByEmail
        summary: Email a purchase magic-link
        description: |
            Emails the caller a magic-link that lets the recipient (the
            same account) complete the purchase outside VR. Same handler
            as the legacy `/token/purchase/{bigMediaId}` route.
        responses:
            "200":
                description: Email queued.
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/try-automatic-order":
    post:
        tags: [Media]
        operationId: tryCreateAutomaticOrder
        summary: Auto-order a free media product
        description: |
            For products priced at $0.00 in the caller's location, creates
            the order automatically without going through Stripe. No-op
            (`200` with a no-op body) when the product has a non-zero
            price.
        requestBody:
            required: true
            content:
                application/json:
                    schema:
                        type: object
                        required: [bigMediaId]
                        properties:
                            bigMediaId: { type: string }
        responses:
            "200":
                description: Order created (or no-op).
                content:
                    application/json:
                        schema:
                            type: object
                            properties:
                                ordered: { type: boolean }
                                entitlement: { $ref: "../components/schemas/Entitlement.yaml" }
                            additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/order":
    post:
        tags: [Media]
        operationId: createOrder
        summary: Purchase a media product (Stripe)
        description: |
            Creates a Stripe-backed order for the supplied media product.
            **Account must be verified** — calls `requiresVerifiedAccount`,
            which returns `401` with code `RequiresVerifiedEmailAddress`
            for unverified accounts (semantically a 403, but historically
            modeled as 401).
        requestBody:
            required: true
            content:
                application/json:
                    schema:
                        type: object
                        required: [bigMediaId]
                        properties:
                            bigMediaId: { type: string }
                            paymentMethodId:
                                type: string
                                description: Stripe `pm_...` id from the client SDK.
                            couponCode: { type: string }
        responses:
            "200":
                description: Order created.
                content:
                    application/json:
                        schema:
                            type: object
                            additionalProperties: true
            "401":
                description: |
                    Missing / invalid token, **or** account is not
                    verified (code `RequiresVerifiedEmailAddress`).
                content:
                    application/json:
                        schema: { $ref: "../public-api.yaml#/components/schemas/Error" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/entitlement/{entitlementClass}":
    parameters:
        - in: path
          name: entitlementClass
          required: true
          schema: { type: string }
    put:
        tags: [Media]
        operationId: createFreeEntitlement
        summary: Provision a free entitlement
        description: |
            Creates a free entitlement of the supplied class on the
            caller's account when one does not already exist. Idempotent.
        responses:
            "200":
                description: Entitlement (created or already-present).
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/entitlement/{entitlementId}/activate":
    parameters:
        - in: path
          name: entitlementId
          required: true
          schema: { type: string }
    put:
        tags: [Media]
        operationId: activateBrowserEntitlement
        summary: Activate a browser entitlement
        description: |
            Activates a browser-flavored entitlement — used by the website
            to convert a "free trial" entitlement into the active browser
            session.
        responses:
            "200":
                description: Activated entitlement.
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/entitlement/browser":
    get:
        tags: [Media]
        operationId: getBrowserEntitlements
        summary: Currently-active browser entitlement (deprecated)
        description: |
            **Deprecated** — prefer `/media/entitlement/features`. Returns
            the browser entitlement currently active on the caller's
            account.
        deprecated: true
        responses:
            "200":
                description: Browser entitlement (or empty).
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/entitlement/features":
    get:
        tags: [Media]
        operationId: getFeatureFlagEntitlements
        summary: Feature-flag entitlements (biggies)
        description: |
            Returns the caller's feature-flag entitlements — the
            "biggies" — used for rollout-style gating.
        responses:
            "200":
                description: Feature-flag entitlements.
                content:
                    application/json:
                        schema:
                            type: array
                            items: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/oculus/order":
    post:
        tags: [Media]
        operationId: createOculusOrder
        summary: Convert an Oculus IAP token into an entitlement
        description: |
            Consumes an Oculus IAP token (returned by the Oculus billing
            SDK on the headset) and writes the corresponding `Entitlement`
            to the caller's account. **Verified account is NOT required**
            for IAP orders — verification happens through the IAP token
            itself.
        requestBody:
            required: true
            content:
                application/json:
                    schema:
                        type: object
                        required: [iapToken]
                        properties:
                            iapToken: { type: string }
                            bigMediaId:
                                type: string
                                description: Optional — server-derived from the IAP token when omitted.
        responses:
            "200":
                description: Entitlement.
                content:
                    application/json:
                        schema: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "409":
                description: IAP token already consumed.
                content:
                    application/json:
                        schema: { $ref: "../public-api.yaml#/components/schemas/Error" }

"/media/oculus/update":
    post:
        tags: [Media]
        operationId: updateOculusPurchases
        summary: Sync any unconsumed Oculus IAPs
        description: |
            Walks the caller's Oculus IAP receipts and writes
            `Entitlement`s for any that have not yet been ingested. Used
            by the VR app on launch to recover after a failed in-flight
            purchase.
        responses:
            "200":
                description: Sync result.
                content:
                    application/json:
                        schema:
                            type: object
                            properties:
                                createdCount: { type: integer }
                                entitlements:
                                    type: array
                                    items: { $ref: "../components/schemas/Entitlement.yaml" }
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/receipts":
    get:
        tags: [Media]
        operationId: getPublicReceipts
        summary: Caller's purchase receipts
        description: Returns the caller's public-facing purchase receipts.
        responses:
            "200":
                description: Receipts.
                content:
                    application/json:
                        schema:
                            type: array
                            items:
                                type: object
                                additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }

"/media/info/{mediaRequest}":
    parameters:
        - in: path
          name: mediaRequest
          required: true
          schema: { type: string }
    get:
        tags: [Media]
        operationId: getMediaInfo
        summary: Public info for a media request
        description: |
            Returns public metadata for the supplied media request
            (resolves the same fallback list as `/media/start`).
        responses:
            "200":
                description: Media info.
                content:
                    application/json:
                        schema:
                            type: object
                            additionalProperties: true
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
            "404": { $ref: "../public-api.yaml#/components/responses/NotFound" }

"/media/analytics/engagement/{videoPosition}":
    parameters:
        - in: path
          name: videoPosition
          required: true
          schema: { type: string }
          description: Position marker — typically a percentage band or millisecond offset.
    get:
        tags: [Media]
        operationId: sendMediaSessionEngagement
        summary: Record media-engagement event
        description: |
            Beacon-style endpoint the VR client calls to record continued
            engagement with a media session at the supplied position
            marker. Drives playback-completion analytics.
        responses:
            "200":
                description: Recorded.
            "401": { $ref: "../public-api.yaml#/components/responses/Unauthorized" }
