/v1/All endpoints are under the /v1/ prefix. Mutating endpoints require an X-Referee-Token header. Browser clients obtain this via POST /v1/auth/request (desktop shows a consent dialog; headless requires the origin to be pre-approved). Set REFEREE_API_TOKEN and REFEREE_ALLOWED_ORIGINS env vars for headless deployments.
Integration Guide
This page provides examples and implementation notes for integrating REFEREE into your web app or streaming workflow.
npm Client Package
The official @cmacrowther/referee-client package bundles TypeScript types, a RefereeClientclass with auto-auth and managed heartbeating, and a plug-and-play React hook — so you don't have to wire up the lifecycle yourself.
npm install @cmacrowther/referee-clientUsage
import { RefereeClient } from '@cmacrowther/referee-client';
import { useReferee } from '@cmacrowther/referee-client/react';
// ─── Vanilla JS / TypeScript ─────────────────────────────────────────────────
const referee = new RefereeClient({ appName: 'MyApp' });
// Token is requested automatically on first authenticated call.
// In desktop mode this triggers a consent dialog; pre-approve
// origins on headless via REFEREE_ALLOWED_ORIGINS env var.
const status = await referee.getStatus();
if (!status.gpuReady) return; // no compatible GPU
// Start session (blocking until HLS output is ready)
const { session, dispose } = await referee.startManagedSession({
url: 'https://example.com/live/stream.m3u8',
streamTitle: 'Friday Night Stream',
});
// session.url is the absolute HLS URL — load it directly in your player
player.src = session.url;
// dispose() stops heartbeat + session and cleans up event listeners
window.addEventListener('pagehide', dispose);
// ─── React ───────────────────────────────────────────────────────────────────
function VideoPlayer({ sourceUrl }) {
const { start, stop, status, playbackUrl } = useReferee({ appName: 'MyApp' });
useEffect(() => {
start({ url: sourceUrl, streamTitle: 'Live Stream' });
return () => stop();
}, [sourceUrl]);
if (status === 'unavailable') return <NativePlayer src={sourceUrl} />;
if (playbackUrl) return <HlsPlayer src={playbackUrl} />;
return <LoadingSpinner />;
}Quick Start
These examples show how to integrate REFEREE into your web app or streaming workflow. The API is designed to be simple and flexible, so you can implement it in any language or framework that can make HTTP requests . Each example is standalone — start with the one that best matches your stack, or mix and match patterns as needed. For a quick start, see the graceful degradation example, which shows how to add REFEREE support without disrupting existing playback for users without compatible GPUs.
const REFEREE_BASE = 'http://localhost:14002';
// ─── Option A: Request a token automatically (recommended) ───────────────────
//
// POST /v1/auth/request triggers the REFEREE desktop consent dialog.
// If the origin is already approved (or pre-set via REFEREE_ALLOWED_ORIGINS),
// the token is returned immediately without user interaction.
// The returned token is persistent — cache it in sessionStorage or memory.
async function requestToken(appName) {
const res = await fetch(`${REFEREE_BASE}/v1/auth/request`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ appName }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
// 403 HEADLESS_MODE — origin not pre-approved on a headless server.
// Add it via REFEREE_ALLOWED_ORIGINS env var or POST /v1/origins.
throw new Error(err.error ?? `Auth failed: ${res.status}`);
}
const { token, persistent } = await res.json();
console.log(`Token ${persistent ? '(saved)' : '(one-time)'}: ${token}`);
return token;
}
// ─── Option B: Use a known token directly ────────────────────────────────────
//
// If you control the server, set REFEREE_API_TOKEN on startup and
// hardcode or inject the token — no auth request needed.
// Retrieve it from:
// • Desktop app: Settings → API Token
// • Headless: REFEREE_API_TOKEN env var → token is printed on startup
// • Headless: POST /v1/auth/rotate-token (generates a new one)
const REFEREE_TOKEN = process.env.REFEREE_API_TOKEN ?? '<your-referee-token>';
// ─── Usage ───────────────────────────────────────────────────────────────────
let token = null;
async function getOrRequestToken() {
if (token) return token;
token = await requestToken('MyApp');
return token;
}
// All authenticated calls pass the token as X-Referee-Token
async function startSession(sourceUrl) {
const t = await getOrRequestToken();
const res = await fetch(`${REFEREE_BASE}/v1/stream/start`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Referee-Token': t,
},
body: JSON.stringify({ url: sourceUrl, appName: 'MyApp' }),
});
return res.json();
}Session Lifecycle
1. GET /v1/status — Verify REFEREE is healthy and GPU is ready
2. POST /v1/stream/start — Blocking call (up to ~3 min) — waits until upscaled HLS output is ready
3. Load HLS URL in player — Begin playback (e.g., hls.js, Video.js)
4. POST /v1/stream/heartbeat/{sessionId} — Send every 10 seconds while playing
5. POST /v1/stream/stop — Stop session when playback ends
Heartbeat Contract
Recommended interval
10 seconds
Server timeout
15 seconds
Missed heartbeats tolerated
~0–1 consecutive
Orphan grace period
5 minutes
Before first heartbeat only. After heartbeats start, server timeout (15 s) applies directly.
CORS & Reverse Proxy
REFEREE sets Access-Control-Allow-Origin: * on GET /v1/status, POST /v1/auth/request, and HLS segment responses, so browsers can access those without issue. All other endpoints (stream start/stop/heartbeat, origins management) only allow cross-origin requests from origins in the approved-origins list — unapproved cross-origin XHR to those endpoints will be blocked by the browser.
Mixed-content block: If your web app is served over https://, browsers will block requests to http://localhost:14002. During development, serve your app over HTTP, or use the nginx reverse proxy below.
nginx reverse proxy (example)
Place this inside your server block to expose REFEREE under your own HTTPS domain.
location /referee/ {
proxy_pass http://localhost:14002/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Allow the browser to consume the HLS stream cross-origin
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, X-Referee-Token" always;
# Required for streaming TS segments without buffering
proxy_buffering off;
proxy_read_timeout 300s;
}Then set REFEREE_BASE = 'https://yourdomain.com/referee' in your integration code.
API Reference
All endpoints accept and return JSON. Base URL: http://localhost:14002
Versioning
All routes are prefixed with /v1/. Future breaking changes will increment the version number, so existing integrations continue to work.
Authentication
Mutating endpoints (POST /v1/stream/start, /heartbeat, /stop, and the /v1/auth/rotate-token and /v1/origins management endpoints) require an X-Referee-Token header.
Desktop app: retrieve the token from the REFEREE settings panel or call POST /v1/auth/request from a browser page — REFEREE shows a consent dialog and returns the token on approval.
Headless server: set the REFEREE_API_TOKEN environment variable (≥ 32 chars) before starting the server to use a known token. Pre-approve origins with REFEREE_ALLOWED_ORIGINS (comma-separated) so browser clients can obtain the token via POST /v1/auth/request without a UI. Unknown origins receive 403 HEADLESS_MODE.
Unauthenticated endpoints: GET /v1/status, GET /v1/ping, GET /v1/tmp/* (HLS segments), and POST /v1/auth/request. Note: GET /v1/origins is token-protected.
Supported Source Formats
Pass any of the following as the url field in POST /v1/stream/start.
| Format | Example | Notes |
|---|---|---|
| HLS (.m3u8) | https://…/stream.m3u8 | Recommended. Live and VOD HLS streams. Supports auth headers. |
| DASH (.mpd) | https://…/manifest.mpd | Best-effort via FFmpeg demuxer — no first-class manifest handling. Behaviour may vary. |
| Direct video URL | https://…/video.mp4 | MP4, MKV, and other FFmpeg-compatible container formats. |
Heads up: HLS segments must end in .ts / .m4s
FFmpeg 7.0+ enforces an allowed_segment_extensions allowlist on HLS playlists and rejects segments whose URL path does not end in one of ts, m4s, etc. Query parameters are not inspected, so a stream-proxy URL like:
https://proxy.example.com/api/stream-proxy?url=…/segment.ts&sig=…will fail metadata probing with URL … is not in allowed_segment_extensions even though the underlying segment is a valid MPEG-TS file.
Fixes (pick one):
- Reshape the proxy URL so the path ends in
.ts/.m4s, e.g./api/stream-proxy/segment.ts?url=…. This is the most portable fix and also keeps third-party HLS players happy. - Rewrite the playlist server-side before returning it, so segment URIs already include the expected extension on the path.
- Pass the original
.m3u8URL toPOST /v1/stream/startwith any required headers (referer,origin, cookies) and let REFEREE fetch segments directly instead of routing through an extension-stripping proxy.
Error Codes
All error responses include a machine-readable code field alongside the human-readable error message:
{
"error": "Session not found",
"code": "SESSION_NOT_FOUND"
}| Status | Meaning | Action |
|---|---|---|
400 INVALID_REQUEST | Missing or invalid request field | Check your request body |
400 MISSING_ORIGIN | No Origin header on auth request | Browsers set this automatically; non-browser callers must add it manually |
400 INVALID_ORIGIN | Origin is not a valid http/https URL | Ensure the Origin header starts with http:// or https:// |
401 UNAUTHORIZED | Missing or invalid API token | Include a valid X-Referee-Token header |
403 NO_APP_HANDLE | Desktop app handle is unavailable | Rare internal state; retry or restart the desktop app |
403 CONSENT_DENIED | User clicked Deny on the consent dialog | Prompt the user to allow access in REFEREE and retry |
403 HEADLESS_MODE | Origin not pre-approved on a headless server | Add origin via POST /v1/origins or REFEREE_ALLOWED_ORIGINS env var |
404 ORIGIN_NOT_FOUND | Origin not in approved list (DELETE) | Check the origin spelling and encoding |
404 SESSION_NOT_FOUND | Session not found | Session was already cleaned up — stop heartbeat |
502 PIPELINE_EXITED | Pipeline exited early | Encoder crashed before producing output — check source URL is reachable |
503 NO_ENCODER | No compatible GPU | REFEREE cannot upscale without a compatible GPU |
504 PIPELINE_TIMEOUT | Pipeline timed out | GPU may be busy or source stream unreachable |
408 CONSENT_TIMEOUT | User did not respond to the consent dialog within 180 s | Retry the auth request — REFEREE will show the dialog again |
429 RATE_LIMITED | Too many requests (5/min for auth, 3/min for stream-start) | Wait for the duration in the Retry-After: 60 response header before retrying |
400 INVALID_URL | Stream URL is not a valid http/https URL | Only http:// and https:// URLs are accepted as the url field |
400 SSRF_BLOCKED | Stream URL resolves to a loopback, private, or link-local address | Use a publicly reachable URL; internal addresses are blocked for security |
400 INVALID_HEADERS | The headers object contains a forbidden header name or a CRLF character in a value | Remove hop-by-hop and forwarding headers (e.g. Host, X-Forwarded-For, Transfer-Encoding) |
FAQ
Common questions from developers integrating REFEREE.