{"openapi":"3.1.0","info":{"title":"Video AI API","version":"1.0.0","description":"The Video AI surface — realtime calling, recording, broadcasting, transcription, AI assistant in-call, end-to-end encrypted messaging.\n\n**Authentication.** `hbs_live_…` API key in the `Authorization: Bearer` header for server-to-server calls; short-lived participant JWTs (minted via `POST /video/v1/tokens`) for client SDKs.\n\n**SDKs.**\n\n* **JavaScript (core)** — `npm install @2stars/video-js` ([npmjs.com/package/@2stars/video-js](https://www.npmjs.com/package/@2stars/video-js) · [github.com/2stars-io/video-js](https://github.com/2stars-io/video-js)). `StarsClient`, rooms, peers, messages, recording, broadcasts, AI in-call. Pure ESM with WebRTC + signaling built in.\n* **React** — `npm install @2stars/video-react` ([npmjs.com/package/@2stars/video-react](https://www.npmjs.com/package/@2stars/video-react) · [github.com/2stars-io/video-react](https://github.com/2stars-io/video-react)). Provider + 25 hooks + ready-made components (`VideoTile`, `RoomGrid`, `ChatStrip`, `WhiteboardSurface`) on top of `@2stars/video-js`.\n* **Android** — Gradle `implementation 'com.github.2stars-io:video-android:0.5.3'` via [JitPack](https://jitpack.io/#2stars-io/video-android) ([github.com/2stars-io/video-android](https://github.com/2stars-io/video-android)). Coroutine-friendly, mediasoup-backed, includes whiteboard, screen share, AI assistant, transcription, translation, virtual backgrounds, foreground service for Doze survival.\n* **Umbrella JS** — `npm install @2stars/sdk` ([npmjs.com/package/@2stars/sdk](https://www.npmjs.com/package/@2stars/sdk)) pulls the JS core + React bindings + VerifAI in one shot.\n* **Backend (any language)** — generate a client in any language from this spec via `openapi-generator-cli`.\n\n### Realtime control surface (Socket.IO)\n\nMost realtime calling features live on the Socket.IO transport — see SDK references for typed wrappers. The currently published event surface includes media negotiation, E2E key bundle relay, AI participant lifecycle, transcription, recording status, and **desktop control via screen-share** (Phase 5B).\n\n**Desktop control events.** When a peer is sharing their screen, another participant can request input control. Sharer-side approval is per-requester; once granted, the controller's pointer / keyboard events relay to the sharer via the SFU signaling channel. State is Redis-backed cross-node, so request / grant / input can each land on any api node.\n\n| Direction | Event | Purpose |\n|---|---|---|\n| C→S | `request-control` | Controller asks `{ target: sharerParticipantId }` for control. Returns `{ ok, requestId }`. Server stores pending request at `hebbs:control:req:<id>` with 60s TTL. |\n| C→S | `grant-control` | Sharer approves `{ requestId }`. Server creates active session at `hebbs:control:act:<roomCode>:<sharerPid>` (1h TTL) plus reverse-index at `hebbs:control:byctl:<roomCode>:<controllerPid>` for O(1) input forwarding. |\n| C→S | `deny-control` | Sharer rejects `{ requestId, reason? }`. |\n| C→S | `revoke-control` | Either side ends an active session. Auto-fires SDK-side when the sharer stops screen-sharing (`reason: 'screenshare-ended'`). |\n| C→S | `remote-input` | Controller fires `{ type, nx, ny, button?, deltaX?, deltaY?, key?, code?, ctrl?, meta?, alt?, shift? }`. SDK client-gates on active grant (drops without grant), throttles `pointermove` to ~30 Hz with coalescing to latest, passes discrete events through. Server sanitizes and relays. |\n| S→C | `control-request-incoming` | Sharer receives `{ requestId, from, fromDisplayName, requestedAt }`. Show a UI prompt. |\n| S→C | `control-granted` | Controller (and sharer) receive the established `ControlSession`. SDK populates `_activeControl` so subsequent `sendRemoteInput` calls are accepted. |\n| S→C | `control-denied` | Controller receives `{ requestId, by, reason }`. |\n| S→C | `control-revoked` | Both sides receive `{ sharer, reason }`. SDK clears `_activeControl` so further `sendRemoteInput` calls drop client-side, closing any race past the server-side revoke. |\n| S→C | `control-active` / `control-inactive` | Room-wide broadcast for status badges in other tiles. |\n\n**Browser sandbox.** JavaScript in a webpage cannot synthesize OS-level mouse / keyboard input. The SDK delivers the normalized event stream to the sharer; how it's acted on is the host app's choice — render a cursor preview (default in the demo), drive a same-tab DOM if the sharer specifically shared the 2Stars tab itself, or hand off to a Chrome extension / native helper for true click-through control on any window. Android consumers should bridge to an `AccessibilityService` to translate events into real input.","contact":{"name":"2Stars","url":"https://2stars.io","email":"hello@2stars.io"},"license":{"name":"Proprietary","identifier":"LicenseRef-Proprietary"}},"servers":[{"url":"https://api.2stars.io","description":"Production"},{"url":"http://localhost:8080","description":"Local dev — note: until DNS rewrite lands, paths are served at /api/v1/* under this host. Replace /video/v1/* with /api/v1/* and /verifai/v1/* with /api/v1/verifai/* when calling the local docker stack."}],"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","bearerFormat":"hbs_live_…","description":"Your live API key. Format `hbs_live_<base64url>`. Test environments may use `hbs_test_<base64url>`. Self-service rotation in the developer dashboard at /dashboard."},"Dashboard":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Dashboard JWT minted by `POST /auth/login`. Used only by the customer console (your dashboard) for self-service operations on the developer's own account. Never ship in client apps."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code","example":"invalid_request"},"message":{"type":"string","description":"Human-readable error message"}}}}},"Pagination":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":500,"default":50},"cursor":{"type":"string","nullable":true,"description":"Opaque cursor returned by the previous page"}}},"Room":{"type":"object","required":["id","roomCode","roomType","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"roomCode":{"type":"string","example":"abc-123-xyz"},"roomType":{"type":"string","enum":["video","voice","messaging"]},"maxParticipants":{"type":"integer","minimum":1,"maximum":1000},"isActive":{"type":"boolean"},"isLocked":{"type":"boolean","description":"Set true via the lock endpoint to prevent new joins"},"e2eRequired":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"closedAt":{"type":"string","format":"date-time","nullable":true},"metadata":{"type":"object","additionalProperties":true}}},"Participant":{"type":"object","required":["id","participantId","joinedAt"],"properties":{"id":{"type":"string","format":"uuid"},"participantId":{"type":"string"},"displayName":{"type":"string","nullable":true},"joinedAt":{"type":"string","format":"date-time"},"leftAt":{"type":"string","format":"date-time","nullable":true},"durationSeconds":{"type":"integer","nullable":true}}},"TokenRequest":{"type":"object","required":["roomCode","participantId"],"properties":{"roomCode":{"type":"string"},"participantId":{"type":"string","description":"Stable, customer-controlled id for this user in this room"},"displayName":{"type":"string","nullable":true},"ttlSeconds":{"type":"integer","minimum":60,"maximum":86400,"default":3600}}},"Token":{"type":"object","required":["token","expiresAt"],"properties":{"token":{"type":"string","description":"Short-lived JWT — give this to the SDK on the client"},"expiresAt":{"type":"string","format":"date-time"}}},"RecordingJob":{"type":"object","required":["id","status","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","recording","stopping","uploading","complete","failed","cancelled"]},"format":{"type":"string","enum":["mp4","webm","wav","ogg","hls"]},"layout":{"type":"string","enum":["audio_only","single_speaker","grid"]},"startedAt":{"type":"string","format":"date-time","nullable":true},"stoppedAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"durationSeconds":{"type":"integer","nullable":true},"storageUrl":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Broadcast":{"type":"object","required":["id","status","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","recording","stopping","uploading","complete","failed"]},"viewerToken":{"type":"string","nullable":true,"description":"Pass to viewers as ?t=<token>; gates HLS playback"},"spotlightProducerId":{"type":"string","nullable":true},"abrRungs":{"type":"array","items":{"type":"object"},"nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Message":{"type":"object","required":["id","roomId","senderId","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"roomId":{"type":"string","format":"uuid"},"senderId":{"type":"string"},"senderDisplayName":{"type":"string","nullable":true},"encryptedContent":{"type":"string","description":"AES-256-GCM ciphertext, base64"},"iv":{"type":"string","description":"Per-message IV, base64"},"createdAt":{"type":"string","format":"date-time"}}},"Webhook":{"type":"object","required":["id","url","events","isActive","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"},"description":"Event names to subscribe to (e.g. `room.created`, `recording.complete`)"},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"UsageWindow":{"type":"object","required":["windowDays","totals"],"properties":{"windowDays":{"type":"integer","minimum":1,"maximum":365},"totals":{"type":"array","items":{"type":"object","required":["recordType","records","total"],"properties":{"recordType":{"type":"string","example":"audio_minute"},"records":{"type":"integer"},"total":{"type":"number","description":"Total quantity for this period"}}}},"series":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","format":"date-time"},"recordType":{"type":"string"},"total":{"type":"number"}}}}}},"ApiKey":{"type":"object","required":["id","keyPrefix","environment","isActive","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"keyPrefix":{"type":"string","description":"First ~16 chars of the key, e.g. `hbs_live_AbCdEfG`"},"name":{"type":"string"},"environment":{"type":"string","enum":["live","test"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":"string","format":"date-time","nullable":true}}},"Region":{"type":"object","required":["id","displayName"],"properties":{"id":{"type":"string","example":"us-east"},"displayName":{"type":"string","example":"US East (N. Virginia)"},"geo":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}}},"turnHosts":{"type":"array","items":{"type":"string"}}}},"TurnCredentials":{"type":"object","required":["username","credential","urls","expiresAt"],"properties":{"username":{"type":"string","description":"HMAC-signed `<expiry>:<roomCode>` per RFC 5766"},"credential":{"type":"string","description":"HMAC-SHA1 of username with the rotating shared secret"},"urls":{"type":"array","items":{"type":"string"},"description":"turn: + turns: URLs in priority order"},"expiresAt":{"type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Allowed but blocked by policy / feature flag / quota.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicts with current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"tags":[{"name":"Rooms","description":"Room lifecycle, summary, intelligence, AI participant."},{"name":"Tokens","description":"Mint short-lived participant JWTs for client-side join."},{"name":"Messages","description":"End-to-end-encrypted chat history."},{"name":"Recording","description":"Server-managed cloud recording, transcripts, highlights."},{"name":"Documents","description":"AI-generated documents from room transcripts (PDF, etc.)."},{"name":"Moderation","description":"Lock rooms, kick / mute / ban participants."},{"name":"Broadcasts (HLS)","description":"Live HLS streaming with viewer-token-gated playback."},{"name":"Webhooks","description":"Outbound event delivery to your server."},{"name":"Usage & quotas","description":"Rolled-up totals, per-day series, per-key quotas."},{"name":"Regions","description":"Public catalog of media-server regions for proximity routing."},{"name":"TURN credentials","description":"HMAC-signed TURN credentials per RFC 5766."},{"name":"API keys","description":"Self-service key management. Dashboard JWT, not API key."},{"name":"Configuration","description":"Self-service Video AI settings — routing-mode toggles, master kill-switch."},{"name":"Developer console","description":"Endpoints used by your dashboard. Dashboard JWT, not API key."},{"name":"Public","description":"No auth required. Useful for region discovery, HLS playback."}],"paths":{"/video/v1/rooms":{"post":{"tags":["Rooms"],"summary":"Create a room","description":"Gated by the `video-enabled` feature flag (default ON). When the developer has turned it OFF via `PATCH /video/v1/config`, this endpoint returns `403` with `error.code = \"feature_disabled\"` and `error.featureName = \"video-enabled\"` — useful as a soft kill-switch for billing-driven suspension or self-service pause. Existing rooms keep running until participants leave.","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"roomType":{"type":"string","enum":["video","voice","messaging"],"default":"video"},"maxParticipants":{"type":"integer","minimum":1,"maximum":1000,"default":10},"metadata":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Room created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Room"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Feature disabled. `error.featureName` names the offending flag (typically `video-enabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/video/v1/rooms/{code}":{"get":{"tags":["Rooms"],"summary":"Get room details","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Room.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Room"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Rooms"],"summary":"Close a room","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"204":{"description":"Closed."},"404":{"$ref":"#/components/responses/NotFound"}}}},"/video/v1/rooms/{code}/participants":{"get":{"tags":["Rooms"],"summary":"List participants","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Participants.","content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"type":"array","items":{"$ref":"#/components/schemas/Participant"}}}}}}}}}},"/video/v1/rooms/{code}/summary":{"get":{"tags":["Rooms"],"summary":"AI-generated post-call summary","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Summary text."},"404":{"$ref":"#/components/responses/NotFound"}}}},"/video/v1/rooms/{code}/intelligence":{"get":{"tags":["Rooms"],"summary":"Conversation intelligence analysis","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Analysis JSON."}}}},"/video/v1/rooms/{code}/ai-participant":{"get":{"tags":["Rooms"],"summary":"AI participant status","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Status."}}},"delete":{"tags":["Rooms"],"summary":"Terminate the AI participant","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"204":{"description":"Terminated."}}}},"/video/v1/tokens":{"post":{"tags":["Tokens"],"summary":"Mint a participant JWT","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"201":{"description":"Token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}}}}},"/video/v1/messages/{roomCode}":{"parameters":[{"name":"roomCode","in":"path","required":true,"schema":{"type":"string"}}],"post":{"tags":["Messages"],"summary":"Send an end-to-end encrypted message","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["encryptedContent","iv","senderId"],"properties":{"senderId":{"type":"string"},"encryptedContent":{"type":"string"},"iv":{"type":"string"}}}}}},"responses":{"201":{"description":"Stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}}}},"get":{"tags":["Messages"],"summary":"Fetch chat history","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Messages.","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}}}}}}}}}},"/video/v1/rooms/{code}/recording/start":{"post":{"tags":["Recording"],"summary":"Start a server-managed recording","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"type":"string","enum":["mp4","webm","wav","ogg","hls"]},"layout":{"type":"string","enum":["audio_only","single_speaker","grid"]}}}}}},"responses":{"201":{"description":"Recording job created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingJob"}}}}}}},"/video/v1/rooms/{code}/recording/stop":{"post":{"tags":["Recording"],"summary":"Stop an active recording","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Stopped.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingJob"}}}}}}},"/video/v1/rooms/{code}/recording/jobs":{"get":{"tags":["Recording"],"summary":"List recording jobs for a room","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Jobs.","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/RecordingJob"}}}}}}}}}},"/video/v1/rooms/{code}/recording/jobs/{id}":{"get":{"tags":["Recording"],"summary":"Get a recording job","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingJob"}}}}}}},"/video/v1/rooms/{code}/recording":{"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"get":{"tags":["Recording"],"summary":"Latest recording for a room","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingJob"}}}}}},"post":{"tags":["Recording"],"summary":"Save recording metadata (legacy upload path)","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"storageUrl":{"type":"string"},"durationSeconds":{"type":"integer"},"fileSizeBytes":{"type":"integer"}}}}}},"responses":{"200":{"description":"Saved."}}}},"/video/v1/rooms/{code}/recording/highlights":{"get":{"tags":["Recording"],"summary":"Highlighted segments from a recording","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"responses":{"200":{"description":"Highlights array."}}}},"/video/v1/rooms/{code}/recording/search":{"get":{"tags":["Recording"],"summary":"Semantic search over recording transcript","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."},{"name":"q","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Search hits."}}}},"/video/v1/recordings/files/{filename}":{"get":{"tags":["Recording"],"summary":"Download a recording file (local-driver only)","security":[{"ApiKey":[]}],"parameters":[{"name":"filename","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"File bytes.","content":{"application/octet-stream":{}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/video/v1/rooms/{code}/documents":{"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"post":{"tags":["Documents"],"summary":"Generate a document from transcript","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["summary","minutes","action_items","custom"]},"prompt":{"type":"string"}}}}}},"responses":{"201":{"description":"Document created."}}},"get":{"tags":["Documents"],"summary":"List generated documents","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Documents."}}}},"/video/v1/rooms/{code}/documents/{id}":{"get":{"tags":["Documents"],"summary":"Get a generated document","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Document."}}}},"/video/v1/rooms/{code}/documents/{id}/pdf":{"get":{"tags":["Documents"],"summary":"Download document as PDF","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"PDF bytes.","content":{"application/pdf":{}}}}}},"/video/v1/rooms/{code}/lock":{"post":{"tags":["Moderation"],"summary":"Lock or unlock a room","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"locked":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated."}}}},"/video/v1/rooms/{code}/moderation/kick":{"post":{"tags":["Moderation"],"summary":"Kick a participant","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participantId":{"type":"string"},"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Kicked."}}}},"/video/v1/rooms/{code}/moderation/mute":{"post":{"tags":["Moderation"],"summary":"Force-mute audio or video","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participantId":{"type":"string"},"kind":{"type":"string","enum":["audio","video"]}}}}}},"responses":{"200":{"description":"Muted."}}}},"/video/v1/rooms/{code}/moderation/ban":{"post":{"tags":["Moderation"],"summary":"Ban a participant","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participantId":{"type":"string"},"reason":{"type":"string"},"untilTimestamp":{"type":"integer","nullable":true}}}}}},"responses":{"200":{"description":"Banned."}}}},"/video/v1/rooms/{code}/moderation/unban":{"post":{"tags":["Moderation"],"summary":"Unban a participant","security":[{"ApiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"participantId":{"type":"string"}}}}}},"responses":{"200":{"description":"Unbanned."}}}},"/video/v1/rooms/{code}/broadcasts":{"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Room code (URL alias `:code`)."}],"post":{"tags":["Broadcasts (HLS)"],"summary":"Start an HLS broadcast","security":[{"ApiKey":[]}],"responses":{"201":{"description":"Broadcast started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Broadcast"}}}}}},"get":{"tags":["Broadcasts (HLS)"],"summary":"List broadcasts for a room","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Broadcasts.","content":{"application/json":{"schema":{"type":"object","properties":{"broadcasts":{"type":"array","items":{"$ref":"#/components/schemas/Broadcast"}}}}}}}}}},"/video/v1/broadcasts/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Broadcasts (HLS)"],"summary":"Broadcast status + viewer token","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Broadcast"}}}}}},"delete":{"tags":["Broadcasts (HLS)"],"summary":"Stop a broadcast","security":[{"ApiKey":[]}],"responses":{"204":{"description":"Stopped."}}}},"/video/v1/broadcasts/{id}/spotlight":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"put":{"tags":["Broadcasts (HLS)"],"summary":"Set spotlight producer","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"producerId":{"type":"string"}}}}}},"responses":{"200":{"description":"Set."}}},"delete":{"tags":["Broadcasts (HLS)"],"summary":"Clear spotlight producer","security":[{"ApiKey":[]}],"responses":{"204":{"description":"Cleared."}}}},"/video/v1/broadcasts/{id}/playlist.m3u8":{"get":{"tags":["Broadcasts (HLS)"],"summary":"HLS master playlist (viewer-token gated)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"t","in":"query","required":true,"schema":{"type":"string"},"description":"Viewer token from `GET /video/v1/broadcasts/:id`"}],"responses":{"200":{"description":"M3U8 playlist.","content":{"application/vnd.apple.mpegurl":{}}}}}},"/video/v1/broadcasts/{id}/{rung}/playlist.m3u8":{"get":{"tags":["Broadcasts (HLS)"],"summary":"HLS variant playlist (ABR rung)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"rung","in":"path","required":true,"schema":{"type":"string"}},{"name":"t","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"M3U8 variant."}}}},"/video/v1/broadcasts/{id}/seg/{filename}":{"get":{"tags":["Broadcasts (HLS)"],"summary":"HLS segment file","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string"}},{"name":"t","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"TS segment.","content":{"video/MP2T":{}}}}}},"/video/v1/broadcasts/{id}/{rung}/seg/{filename}":{"get":{"tags":["Broadcasts (HLS)"],"summary":"HLS variant segment file","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"rung","in":"path","required":true,"schema":{"type":"string"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string"}},{"name":"t","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"TS segment."}}}},"/video/v1/webhooks":{"post":{"tags":["Webhooks"],"summary":"Register a webhook endpoint","security":[{"Dashboard":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}}}},"get":{"tags":["Webhooks"],"summary":"List your webhook endpoints","security":[{"Dashboard":[]}],"responses":{"200":{"description":"Webhooks.","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}}}}},"/video/v1/webhooks/{id}":{"delete":{"tags":["Webhooks"],"summary":"Delete a webhook endpoint","security":[{"Dashboard":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted."}}}},"/video/v1/usage":{"get":{"tags":["Usage & quotas"],"summary":"Rolled-up totals + per-day series","security":[{"Dashboard":[]}],"parameters":[{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":365,"default":30}}],"responses":{"200":{"description":"Usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageWindow"}}}}}}},"/video/v1/usage/details":{"get":{"tags":["Usage & quotas"],"summary":"Detailed usage records","security":[{"Dashboard":[]}],"responses":{"200":{"description":"Records."}}}},"/video/v1/usage/quotas":{"get":{"tags":["Usage & quotas"],"summary":"List quotas across your keys","security":[{"Dashboard":[]}],"responses":{"200":{"description":"Quotas."}}}},"/video/v1/usage/quotas/{keyId}/{kind}":{"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string"}}],"put":{"tags":["Usage & quotas"],"summary":"Set or update a monthly quota","security":[{"Dashboard":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"monthlyLimit":{"type":"number"},"hardCap":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated."}}},"delete":{"tags":["Usage & quotas"],"summary":"Remove a quota cap","security":[{"Dashboard":[]}],"responses":{"204":{"description":"Removed."}}}},"/video/v1/usage/me":{"get":{"tags":["Usage & quotas"],"summary":"Current key's usage + quota state","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Per-key state."}}}},"/video/v1/regions":{"get":{"tags":["Regions"],"summary":"Public region catalog","responses":{"200":{"description":"Regions.","content":{"application/json":{"schema":{"type":"object","properties":{"regions":{"type":"array","items":{"$ref":"#/components/schemas/Region"}}}}}}}}}},"/video/v1/regions/probe":{"get":{"tags":["Regions"],"summary":"Latency probe (no-op for RTT measurement)","responses":{"200":{"description":"OK."}}}},"/video/v1/turn-credentials":{"get":{"tags":["TURN credentials"],"summary":"HMAC-signed TURN creds (RFC 5766)","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Creds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnCredentials"}}}}}},"post":{"tags":["TURN credentials"],"summary":"HMAC-signed TURN creds (POST variant)","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Creds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnCredentials"}}}}}}},"/video/v1/turn-credentials/raw":{"get":{"tags":["TURN credentials"],"summary":"Raw username + credential pair","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Pair.","content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string"},"credential":{"type":"string"}}}}}}}}},"/video/v1/keys":{"get":{"tags":["API keys"],"summary":"List your API keys","security":[{"Dashboard":[]}],"responses":{"200":{"description":"Keys.","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}}}}}},"post":{"tags":["API keys"],"summary":"Create a live key (one per dev)","security":[{"Dashboard":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"201":{"description":"Created. Full secret returned ONCE.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"key":{"type":"string","description":"The full hbs_live_… secret. Shown only on creation."}}}]}}}}}}},"/video/v1/keys/rotate":{"post":{"tags":["API keys"],"summary":"Atomic revoke-and-replace","security":[{"Dashboard":[]}],"responses":{"201":{"description":"New key.","content":{"application/json":{"schema":{"$ref":"#/paths/~1video~1v1~1keys/post/responses/201/content/application~1json/schema"}}}}}}},"/video/v1/keys/{id}":{"delete":{"tags":["API keys"],"summary":"Revoke a key","security":[{"Dashboard":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Revoked."}}}},"/video/v1/config":{"get":{"tags":["Configuration"],"summary":"Read Video AI routing toggles for the developer's API key","description":"3.2.0+. Returns the three Video AI routing-mode flags resolved off the developer's primary API key. All three default ON; flipping any to OFF blocks the corresponding flow. Use to render a settings UI without an admin round-trip.","security":[{"Dashboard":[]}],"responses":{"200":{"description":"Config.","content":{"application/json":{"schema":{"type":"object","properties":{"hasKey":{"type":"boolean","description":"Developer has at least one provisioned API key. When false, the three flags default to true and PATCH returns 400."},"videoEnabled":{"type":"boolean","description":"`video-enabled` — master kill-switch. When OFF, `POST /video/v1/rooms` returns 403 `feature_disabled`. Existing rooms keep running."},"p2pAllowed":{"type":"boolean","description":"`video-p2p-allowed` — when OFF, P2P-mode joins (1-2 participants in a non-satellite room) are rejected with reason `p2p_disabled`. Does NOT auto-upgrade to SFU."},"sfuAllowed":{"type":"boolean","description":"`video-sfu-allowed` — when OFF, SFU-mode joins (3+ participants, or satellite-pipe rooms) are rejected with reason `sfu_disabled`."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"patch":{"tags":["Configuration"],"summary":"Toggle Video AI routing modes on the developer's own key","description":"Each field is optional; only the flags present in the body are updated. Returns the same shape as GET.","security":[{"Dashboard":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"videoEnabled":{"type":"boolean"},"p2pAllowed":{"type":"boolean"},"sfuAllowed":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated config (same shape as GET).","content":{"application/json":{"schema":{"$ref":"#/paths/~1video~1v1~1config/get/responses/200/content/application~1json/schema"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}}}}