openapi: 3.1.0

info:
    title: Bigscreen Cloud API (non-admin)
    version: "0"
    summary: VR-client, website, and media-server endpoints on cloud_api.
    description: |
        ## ⚠️ This is the NON-ADMIN reference

        Endpoints exposed by [`cloud/cloud_api/cloud_api.ts`](../../cloud/cloud_api/cloud_api.ts)
        for end-users, the website, and media-server instances. This spec deliberately **does not**
        cover `/admin/*` — those are documented separately at
        [Bigscreen Cloud Admin API](./scalar/cloud.html). The two surfaces use different bearer
        keys and target different audiences:

        | Surface | Bearer key | Audience | Spec |
        |---------|-----------|----------|------|
        | **Non-admin** (this spec) | `cloud-api`, `media-server-api`, `hyperbeam-webhook` | VR clients, website, media servers, Hyperbeam | here |
        | **Admin** | `cloud-admin-api` | Arda, internal tools | [cloud-admin-api](./scalar/cloud.html) |

        Mixing the two — calling a `/admin/*` route with a `cloud-api` bearer, or a `/social/*`
        route with the `cloud-admin-api` bearer — is a 401. The keys are issued separately.

        ## Auth handlers

        `cloud_api` registers four non-admin auth handlers, each backing a distinct bearer-key
        identity:

        | Handler | `apiName` | Used by | Path patterns |
        |---------|-----------|---------|---------------|
        | `standardAuthHandler` | `cloud-api` | VR clients, website | most paths — `/room*`, `/social/*`, `/screen*`, `/activity*`, `/lobby/*`, `/media/*`, `/channel*`, `/connection`, `/schemas`, `/report/*` |
        | `mediaServerAuthHandler` | `media-server-api` | media_server_next instances | `/api/media-server/*` |
        | `websiteAuthHandler` | `website-cloud-api` | website-only flows | a subset of `/apps/browser/*` |
        | `hyperbeamAuthHandler` | `hyperbeam-webhook` | Hyperbeam webhooks | `/hyperbeam/webhook/*` |

        ## Auth at a glance

        Most paths require **two** headers via the standard handler:

        | Header | Value |
        |--------|-------|
        | `Authorization` | `Bearer <cloud-api key>` |
        | `x-access-token` | A JWT obtained from the public auth API |

        Media-server and Hyperbeam paths use their own bearer keys and do **not** require an
        access token. See each operation's `security:` block for the exact scheme.

        ## Spec layout

        Source is split per logical module under `docs/openapi/cloud-public-modules/*.yaml`,
        joined by `$ref` from this root file. Bundle with:

        ```bash
        npx -y @redocly/cli@latest bundle docs/openapi/cloud-api.yaml -o docs/openapi/cloud-api.bundled.yaml
        ```

servers:
    - url: http://localhost:3002
      description: Local dev (started by `yarn dev`).
    - url: https://cloud-api.internal.bigscreenvr.com
      description: Production (network-restricted).

tags:
    - name: Meta
      description: Connection probe and schema enumerations — used to bootstrap clients.
    - name: Rooms
      description: |
          Room create / join / update / delete plus per-room data, seat assignment,
          and user kick. Powers the multiplayer lobby and room-management surfaces.
    - name: Channels
      description: Bigscreen-curated channel groupings — list, fetch one, join.
    - name: Joining
      description: Quick-join entry points — media showings, the default lobby.
    - name: Screens
      description: Screen CRUD — create, list, get, update, attach to / detach from rooms, manage media sessions on a screen.
    - name: Activities
      description: Shared activity sessions (games, co-watching) — CRUD and listing.
    - name: Social
      description: |
          Social graph for end-users — profile lookup, history, friends, blocks,
          notifications, search. Mostly read-only from this surface; mutations are
          gated by the user's own profile state.
    - name: Reports
      description: User and room reports — file complaints surfaced to moderation tools.
    - name: Browser sharing
      description: |
          Hyperbeam-backed shared-browser activities. Unity writes settings; the
          website reads. Two different bearer schemes (standard + website).
    - name: Hyperbeam
      description: Webhook callbacks from the Hyperbeam access-control system. Authenticated by the `hyperbeam-webhook` bearer.
    - name: Media Server
      description: |
          HTTP-side bridge between media servers and cloud_api. Servers register,
          fetch the current manifest, and exchange messages over HTTP because some
          servers live outside AWS and can't reach SQS directly.

paths:
    /connection:
        $ref: "./cloud-public-modules/meta.yaml#/~1connection"
    /schemas:
        $ref: "./cloud-public-modules/meta.yaml#/~1schemas"

    /rooms/latest:
        $ref: "./cloud-public-modules/rooms.yaml#/~1rooms~1latest"
    /rooms/join:
        $ref: "./cloud-public-modules/rooms.yaml#/~1rooms~1join"
    /room:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room"
    /room/{roomId}:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}"
    /rooms:
        $ref: "./cloud-public-modules/rooms.yaml#/~1rooms"
    /room/{roomId}/join:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}~1join"
    /room/{roomId}/data:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}~1data"
    /room/{roomId}/data/{key}:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}~1data~1{key}"
    /room/{roomId}/seat/{seatIndex}:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}~1seat~1{seatIndex}"
    /room/{roomId}/users/{userSessionId}/kick:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}~1users~1{userSessionId}~1kick"
    /room/{roomId}/leave:
        $ref: "./cloud-public-modules/rooms.yaml#/~1room~1{roomId}~1leave"

    /channels/{version}/{name}:
        $ref: "./cloud-public-modules/channels.yaml#/~1channels~1{version}~1{name}"
    /channel/info/{channel_id}:
        $ref: "./cloud-public-modules/channels.yaml#/~1channel~1info~1{channel_id}"
    /channel/join:
        $ref: "./cloud-public-modules/channels.yaml#/~1channel~1join"

    /media/join:
        $ref: "./cloud-public-modules/joining.yaml#/~1media~1join"
    /lobby/join:
        $ref: "./cloud-public-modules/joining.yaml#/~1lobby~1join"

    /screen:
        $ref: "./cloud-public-modules/screens.yaml#/~1screen"
    /screen/{screenId}:
        $ref: "./cloud-public-modules/screens.yaml#/~1screen~1{screenId}"
    /screen/{screenId}/session:
        $ref: "./cloud-public-modules/screens.yaml#/~1screen~1{screenId}~1session"
    /screens:
        $ref: "./cloud-public-modules/screens.yaml#/~1screens"
    /room/{roomId}/screen/{screenId}:
        $ref: "./cloud-public-modules/screens.yaml#/~1room~1{roomId}~1screen~1{screenId}"

    /activity:
        $ref: "./cloud-public-modules/activities.yaml#/~1activity"
    /activity/{activityId}:
        $ref: "./cloud-public-modules/activities.yaml#/~1activity~1{activityId}"
    /activities:
        $ref: "./cloud-public-modules/activities.yaml#/~1activities"

    /social/rooms:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1rooms"
    /social/history:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1history"
    /social/friends:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1friends"
    /social/blocked:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1blocked"
    /social/profile/{socialId}:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1profile~1{socialId}"
    /social/profile/{socialId}/block:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1profile~1{socialId}~1block"
    /social/profile/{socialId}/unblock:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1profile~1{socialId}~1unblock"
    /social/profile/{socialId}/remove_friend:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1profile~1{socialId}~1remove_friend"
    /social/notification:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1notification"
    /social/notifications:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1notifications"
    /social/notification/{notificationId}:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1notification~1{notificationId}"
    /social/notification/{notificationId}/read:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1notification~1{notificationId}~1read"
    /social/notification/{notificationId}/accept:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1notification~1{notificationId}~1accept"
    /social/notification/{notificationId}/decline:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1notification~1{notificationId}~1decline"
    /social/search:
        $ref: "./cloud-public-modules/social.yaml#/~1social~1search"

    /report/user:
        $ref: "./cloud-public-modules/reports.yaml#/~1report~1user"
    /report/room:
        $ref: "./cloud-public-modules/reports.yaml#/~1report~1room"

    /apps/browser/{activityId}:
        $ref: "./cloud-public-modules/browser.yaml#/~1apps~1browser~1{activityId}"

    /hyperbeam/webhook/{activityId}:
        $ref: "./cloud-public-modules/hyperbeam.yaml#/~1hyperbeam~1webhook~1{activityId}"

    /api/media-server/register:
        $ref: "./cloud-public-modules/media-server.yaml#/~1api~1media-server~1register"
    /api/media-server:
        $ref: "./cloud-public-modules/media-server.yaml#/~1api~1media-server"
    /api/media-server/manifest:
        $ref: "./cloud-public-modules/media-server.yaml#/~1api~1media-server~1manifest"
    /api/media-server/messages:
        $ref: "./cloud-public-modules/media-server.yaml#/~1api~1media-server~1messages"
    /api/media-server/message:
        $ref: "./cloud-public-modules/media-server.yaml#/~1api~1media-server~1message"

components:
    securitySchemes:
        cloudApiKey:
            type: http
            scheme: bearer
            bearerFormat: CloudApiKey-or-OAuthJWT
            description: |
                Bearer key for the `cloud-api` application. Most non-admin paths
                require this **plus** `x-access-token`.
        cloudAccessToken:
            type: apiKey
            in: header
            name: x-access-token
            description: |
                JWT for the end-user account the request acts on behalf of. Required
                alongside `cloudApiKey` for VR / website paths.
        mediaServerApiKey:
            type: http
            scheme: bearer
            description: |
                Bearer key for `media-server-api`. Used by `media_server_next`
                instances to register, fetch the manifest, and exchange messages.
                No per-user access token — servers identify themselves via headers
                checked by `MediaServerApi.checkMediaServerHeaders`.
        websiteApiKey:
            type: http
            scheme: bearer
            description: Bearer key for the `website-cloud-api` application. Used by website-specific browser-sharing reads.
        hyperbeamApiKey:
            type: http
            scheme: bearer
            description: Bearer key for `hyperbeam-webhook`. Used by Hyperbeam to call back into cloud_api during browser-sharing access checks.

    responses:
        Unauthorized:
            description: Missing or invalid bearer / access token.
            content:
                application/json:
                    schema: { $ref: "#/components/schemas/Error" }
        Forbidden:
            description: Authenticated but lacks the required policy or relationship.
            content:
                application/json:
                    schema: { $ref: "#/components/schemas/Error" }
        TooManyRequests:
            description: Rate-limited.
            content:
                application/json:
                    schema: { $ref: "#/components/schemas/Error" }

    schemas:
        Error:
            type: object
            required: [error]
            properties:
                error: { type: string }
                details:
                    description: Optional structured details.
            example:
                error: "Forbidden"
