# Bigscreen OpenAPI Reference

OpenAPI 3.1 specs for the Bigscreen internal APIs, rendered with [Scalar](https://github.com/scalar/scalar). **Five separate specs**, deliberately kept apart because each targets a different audience and uses a different bearer key. The `apps/api` service is the exception — it's split into `auth-api` and `public-api` for documentation, but both halves run on the same process and share the same `api` bearer:

| Spec | Source | Endpoints | Audience | Bearer key |
|------|--------|-----------|----------|-----------|
| `auth-api.yaml` | `apps/api/api.ts` (auth + OAuth) | **33** | VR clients, website, Beyond | `api` |
| `public-api.yaml` | `apps/api/api.ts` (everything else) | **52** | VR clients, website, Beyond, Shopify | `api` |
| `admin-api.yaml` | `apps/admin_api/admin_api.ts` | **273** | Arda staff, internal tools, factory | `admin-api` |
| `cloud-admin-api.yaml` | `cloud/cloud_api/cloud_api.ts` (`/admin/*`) | **41** | Arda staff, internal tools | `cloud-admin-api` |
| `cloud-api.yaml` | `cloud/cloud_api/cloud_api.ts` (non-`/admin`) | **62** | VR clients, website, media servers, Hyperbeam | `cloud-api`, `media-server-api`, `website-cloud-api`, `hyperbeam-webhook` |

The cloud-api file is split from the cloud-admin-api file deliberately — the two surfaces use different bearer keys, target different audiences, and mixing them produces 401s. Don't merge them.

## Layout

```
docs/openapi/
    auth-api.yaml                   # apps/api auth + OAuth root spec
    auth-api.bundled.yaml           # generated artifact for the renderer
    public-api.yaml                 # apps/api non-auth root spec
    public-api.bundled.yaml
    admin-api.yaml                  # apps/admin_api root spec
    admin-api.bundled.yaml
    cloud-admin-api.yaml            # cloud/cloud_api admin root spec
    cloud-admin-api.bundled.yaml
    cloud-api.yaml                  # cloud/cloud_api non-admin root spec
    cloud-api.bundled.yaml
    auth-modules/                   # apps/api AUTH modules (4 files)
        oauth.yaml              auth.yaml               accounts.yaml
        password.yaml
    public-modules/                 # apps/api NON-AUTH modules (9 files)
        info.yaml               beyond.yaml             channels.yaml
        topics.yaml             analytics.yaml          media.yaml
        reports.yaml            eye-tracking.yaml       shopify.yaml
    modules/                        # apps/admin_api modules (19 files)
        accounts.yaml           channels.yaml           cnc.yaml
        dhl.yaml                fabricator-jobs.yaml    fabricator-orders.yaml
        fabricator-scans.yaml   fusion.yaml             health.yaml
        inventory.yaml          knowledge-base.yaml     media.yaml
        moderation.yaml         oauth.yaml              pose.yaml
        rdc.yaml                shipping.yaml           topics.yaml
        workers.yaml
    cloud-modules/                  # cloud/cloud_api ADMIN modules (7 files)
        stats.yaml              rooms.yaml              accounts.yaml
        network.yaml            screens.yaml            activities.yaml
        social.yaml
    cloud-public-modules/           # cloud/cloud_api NON-ADMIN modules (11 files)
        meta.yaml               rooms.yaml              channels.yaml
        joining.yaml            screens.yaml            activities.yaml
        social.yaml             reports.yaml            browser.yaml
        hyperbeam.yaml          media-server.yaml
    components/
        schemas/                    # shared schemas $ref'd from any spec
                                    # (LiveRoom, MediaServer, Activity, etc.
                                    # are used by both cloud specs;
                                    # MediaProduct, Channel, ChannelGroup,
                                    # BigOrder, ScanRequest are shared by
                                    # auth-api and admin-api;
                                    # AdminAccount appears in admin-api and
                                    # cloud-admin-api).
    scalar/
        index.html                  # plain HTML landing — five big cards linking
                                    # to the per-spec render pages below
        auth.html                   # renders auth-api.bundled.yaml
        public.html                 # renders public-api.bundled.yaml
        admin.html                  # renders admin-api.bundled.yaml
        cloud.html                  # renders cloud-admin-api.bundled.yaml
        cloud-public.html           # renders cloud-api.bundled.yaml
```

## Editing the specs

1. Edit the relevant module file:
   - auth-api → `auth-modules/<name>.yaml`
   - public-api → `public-modules/<name>.yaml`
   - admin-api → `modules/<name>.yaml`
   - cloud-admin-api → `cloud-modules/<name>.yaml`
   - cloud-api → `cloud-public-modules/<name>.yaml`

   Edit a root file directly only for security schemes / tags / cross-cutting components.
2. Validate every spec you touched:
   ```bash
   yarn docs:lint
   ```
   (One-spec form: `npx -y @redocly/cli@latest lint docs/openapi/<spec>.yaml`.)
   Zero errors required. Warnings (e.g. missing `info.contact`) are acceptable.
3. Regenerate the bundled artifact each spec's renderer reads:
   ```bash
   yarn docs:bundle               # all five
   yarn docs:bundle:admin-api     # one spec — pick from auth-api / public-api / admin-api / cloud-admin-api / cloud-api
   ```

The bundle command also fails on broken `$ref`s, so it doubles as ref validation.

## Viewing the spec

Serve `docs/openapi/` over HTTP, then open `/scalar/` in your browser.

```bash
yarn docs:serve                              # easiest — port 8000
```

Or use any static HTTP server. The `-d` flag below is *relative to your shell's current working directory*:

```bash
# From inside docs/openapi (no -d needed)
python -m http.server 8000

# From the repo root
python -m http.server 8000 -d docs/openapi

# Or, equivalently, with Node
npx -y http-server docs/openapi -p 8000     # repo root
npx -y http-server -p 8000                  # inside docs/openapi
```

Then open:
- <http://localhost:8000/scalar/> — **landing page**: five cards linking to each spec's render
- <http://localhost:8000/scalar/auth.html> — apps/api **auth + OAuth** surface
- <http://localhost:8000/scalar/public.html> — apps/api **non-auth** surface
- <http://localhost:8000/scalar/admin.html> — apps/admin_api
- <http://localhost:8000/scalar/cloud.html> — cloud_api **admin** endpoints
- <http://localhost:8000/scalar/cloud-public.html> — cloud_api **non-admin** endpoints

(The landing is plain HTML — no Scalar bundle, instant load. Each card opens the per-spec Scalar page.)

## Per-endpoint translation contract

When converting a markdown endpoint to OpenAPI:

- `### \`METHOD /path\`` (markdown) → path key + operation method. Convert `:id` to `{id}`.
- First paragraph after the heading → `summary` (one line) + `description` (rest, supports markdown).
- `**Auth:**` line → `x-access-policies` array.
- Path prefix → `x-scopes` array. Use the prefix → scope map at `~/.claude/plans/migrate_admin_api.js` (verb-aware: `read` for GET, `write` otherwise) — same map used by `admin_api.ts`'s `requireScopeAndPolicy({ scopes, policies })` calls.
- Module intro paragraph → `tags[].description` (in root `admin-api.yaml`).

Examples are added when the markdown already has them or a test file in `_test-coverage.md` trivially supplies a payload. Don't fabricate examples.

## Optional path segments

Express path-to-regexp `{/:foo}` syntax (optional segments) is not natively expressible in OpenAPI. The spec registers the variant **with** the optional segment as a path parameter and notes the optional behavior in the description. Affects:

- `/admin/logs/{logType}` (optional `logType`)
- `/admin/media/showings/{synchronizationInterval}/{past}/{future}` (optional `past`, `future`)
- `/admin/shop/sync/{shopifyOrderId}` (handler is a stub anyway)
- `/admin/fabricator/scan_request/{scanRequestId}/topology/{topologyPatientIdOverride}` (optional override)

## Out of scope (deferred)

- Spec-vs-code drift script (`check_*_spec_drift.ts`) — would compare route registrations in `apps/*.ts` / `cloud/cloud_api/cloud_api.ts` against documented paths, fail CI on drift.
- Hand-written `x-codeSamples` for action-dispatch endpoints.
- Static hosting from `arda-server`.

## Coverage check

```bash
# Total operations in each bundled spec
grep -cE "^    (get|post|put|delete|patch):" docs/openapi/auth-api.bundled.yaml          # 33
grep -cE "^    (get|post|put|delete|patch):" docs/openapi/public-api.bundled.yaml        # 52
grep -cE "^    (get|post|put|delete|patch):" docs/openapi/admin-api.bundled.yaml         # 273
grep -cE "^    (get|post|put|delete|patch):" docs/openapi/cloud-admin-api.bundled.yaml   # 41
grep -cE "^    (get|post|put|delete|patch):" docs/openapi/cloud-api.bundled.yaml         # 62
```

For each service, count registered routes (excluding `api.use(...)` middleware) with:

```bash
# apps/api — count distinct (verb, path) pairs to cancel out the OAuth
# if/else duplication. Auth + Public should sum to this number.
grep -nE 'api\.(get|post|put|delete|patch)\("' apps/api/api.ts \
    | grep -oE '(get|post|put|delete|patch)\("\/[^"]+"' | sort -u | wc -l   # 85 (= 33 + 52)

# admin endpoints (cloud-admin-api)
grep -cE '^api\.(get|post|put|delete|patch)\("/admin' cloud/cloud_api/cloud_api.ts        # 41

# non-admin endpoints (cloud-api)
grep -E  '^api\.(get|post|put|delete|patch)\("/' cloud/cloud_api/cloud_api.ts | grep -vc '"/admin'  # 62
```
