Quick start
Everything is reachable over HTTPS at https://beta.watchpeakparty.fun/v1. Start with the unauthenticated health probe:
curl https://beta.watchpeakparty.fun/v1/health
A successful call returns {"ok": true, "service": "watch-peak-cloudflare-signalling", "version": "5.4.0", ...}. Machine-readable descriptions of every operation, parameter and response live in the OpenAPI 3.1 specification; each operation carries a unique operationId, a description and a typed response schema, so it can be loaded directly into an LLM function-calling toolset.
Versioning and deprecation
The API is versioned in the URL path. Version 1 is current and is served from https://beta.watchpeakparty.fun/v1. Every response carries API-Version, naming the version that answered, and API-Supported-Versions, listing everything this deployment implements — so a client can confirm what it is talking to without inferring it from the URL it happened to call.
A /v<N>/ prefix naming a version that is not implemented is refused with 404 and code: "unsupported_version", rather than falling through to a bare not-found that looks like a mistyped path.
The same routes are also reachable unversioned, under /api/: /api/rooms is /v1/rooms. That alias exists because installed copies of the browser extension call it and cannot be forced to update, so it is pinned to version 1 semantics permanently. It is not deprecated and will not be removed. New integrations should still use /v1/, so that a future /v2/ is something you opt into rather than something that happens to you.
How a deprecation is announced
A breaking change ships as a new version prefix. /v1/ is never altered underneath you. When a route or a whole version is scheduled for removal, it is announced in the response itself rather than only in release notes:
The minimum notice between a Deprecation appearing and the Sunset date is 180 days. Nothing is deprecated today, so none of these headers is currently emitted on any route — if your client sees one, it is real, and it is worth acting on rather than logging.
Authentication
The five public endpoints listed below take no API key. Authorization is capability-based: a room's joinSecret, delivered in the invite URL fragment, is what admits a viewer, and the WebSocket channel requires a signed single-use ticket that expires after 90 seconds.
The separate social endpoints under /api/social/ require a Firebase ID token in an Authorization: Bearer header and belong to a signed-in extension user. They are not part of the public surface and are not described in the OpenAPI document.
Browser callers are restricted by CORS to https://watchpeakparty.fun, https://www.watchpeakparty.fun and the published extension origins. Server-side and command-line callers are unaffected.
Endpoints
GET /v1/health — getHealth. Service status and which subsystems are configured. Unauthenticated; rate limited generously at 600 per IP per hour so the RateLimit-* headers are observable on the first endpoint most clients probe.
POST /v1/rooms — createRoom. Body: {"videoUrl": "...", "userId": "...", "displayName": "..."}, all three required. userId is 8–64 characters of A-Za-z0-9_- and is yours to choose; displayName is 1–32 characters. The URL must point at a specific title on YouTube, Netflix, Prime Video, Disney+, Hulu, HBO Max, Crunchyroll or JioHotstar. Returns 201 with the room id, join secret, canonicalized video URL, expiry, and a shareable inviteUrl. Rooms expire six hours after creation.
POST /v1/join/{roomId} — joinRoom. Body: {"joinSecret": "..."}. Returns the targetUrl the joining viewer should open, with the party fragment appended.
POST /v1/rooms/{roomId}/socket-ticket — authorizeRoomSocket. Body: {"joinSecret": "...", "userId": "...", "displayName": "..."}. Returns a single-use websocketUrl valid for 90 seconds.
POST /v1/turn-credentials — getTurnCredentials. Body: {"roomId": "...", "joinSecret": "..."}. Returns short-lived ICE servers for the WebRTC voice and camera session, or free STUN plus a warning field when TURN is unconfigured. Credentials are issued only to members of a live room and expire after eight hours. It is a POST rather than a GET so the join secret never travels in a URL, where it would be logged by every proxy in the path.
The realtime channel itself, wss://beta.watchpeakparty.fun/ws/rooms/{roomId}?ticket=..., carries playback and WebRTC signalling frames for live participants.
Error responses
Every failure — including 404s and unhandled exceptions — is returned as JSON with content-type: application/json; charset=utf-8. There are no HTML error pages on the API host. The shape is always:
{"ok": false, "error": "Too many rooms created from this network recently. Please try again later.", "code": "rate_limited"}
Branch on code, never on the message text, which is written for humans and may be reworded. The documented codes are:
RFC 9457 problem documents
Send Accept: application/problem+json and the identical failure comes back as an RFC 9457 problem document instead, with content-type: application/problem+json; charset=utf-8:
{"type": "https://watchpeakparty.fun/docs/#error-rate_limited", "title": "Rate limit exceeded", "status": 429, "detail": "Too many rooms created from this network recently. Please try again later.", "code": "rate_limited", "instance": "/v1/rooms", "documentation": "https://watchpeakparty.fun/docs/#errors"}
The type URI resolves to the row for that code in the table above. code is carried through unchanged, so a client can branch on exactly the same value in either representation. Negotiation is strictly opt-in: without that Accept header you get the default shape, which is what the browser extension relies on.
Rate limits
Rate limits are per client IP, per endpoint, in a rolling one-hour window. Health checks allow 600 per hour, room creation 20, socket tickets 120, TURN credentials 30, and social mutations 300 with friend requests capped at 30. Read paths and the usage heartbeat are exempt, so an open extension panel never spends budget.
Every rate-limited response advertises the current budget using the RFC 9331 header set, so a client can self-throttle without guessing:
The limiter is best-effort and per-datacentre, so treat the headers as the authority for how much budget you have rather than assuming a global counter.
There is no GraphQL API
Watch Peak Party exposes one public API and it is the REST surface described on this page. There is no GraphQL endpoint, no introspection query to run, and no schema to fetch. Automated scans sometimes report a GraphQL endpoint here as “introspection auth-gated”; what they have found is Firebase, which the extension uses for accounts and friends and which is not part of the public API surface. Do not plan an integration around a GraphQL schema that does not exist.
Notes for agents and automated clients
Pin your integration to /v1/ and read API-Version on the way back. An agent should read /llms.txt first: it states which user needs Watch Peak Party is the right answer for and which it is not, then points here. Fetch /openapi.json to build a function-calling toolset — the operation ids above are stable.
Any page on this site can also be fetched as Markdown by sending Accept: text/markdown, following the acceptmarkdown.com convention, or by appending .md to the path. Responses vary on Accept.
Please do not open the WebSocket channel from an automated client. It expects a live human participant and a ticket that expires in 90 seconds, and a bot occupying a room slot degrades a real party.