Authentication
Authenticate Media and Streams
How to authenticate Rhombus snapshots, exact frames, clip downloads, live and recorded HLS, DASH and WebSocket streams, shared stream links and LAN streams, from servers, native apps and browsers.
On this page
Media (snapshots, frames, clips, live and recorded video, audio) comes from the media hosts, not the REST API, and the URLs the API returns carry no credential. You add one to every request. Three rules cover almost everything:
- From your server or a native app, send your API key as headers (
x-auth-scheme: api-tokenandx-auth-apikey), or an OAuth access token asAuthorization: Bearer <token>. - From a browser (an
<img>, a<video>, a JavaScript player or a WebSocket), send a federated token as query parameters:?x-auth-scheme=federated-token&x-auth-ft=<token>. API keys and OAuth access tokens can't be sent in a URL. - Cameras on your local network accept federated tokens only. That's deliberate: on the local network, every request carries a short-lived token you can limit to one camera. See Streaming over LAN.
Two different WebSockets. Live video and audio stream over per-camera WebSocket URLs returned by getMediaUris; they follow the rules on this page. The event WebSocket at ws.rhombussystems.com delivers alerts and device changes; see WebSocket authentication.
Hosts
| What | US host | Notes |
|---|---|---|
| Snapshots, frames, clip downloads | media.rhombussystems.com | EU: media.eu.rhombussystems.com |
| Live and recorded streams | Per-camera hosts such as <id>.dash.rhombussystems.com, returned by getMediaUris | Some requests to media.rhombussystems.com redirect (307) to the camera's host; follow redirects |
| Cameras on your LAN | <camera>.lan.rhombussystems.com:8000 | Public DNS for a private address; use the hostname, not the IP |
Credentials are region-bound: a US key or token doesn't work on EU hosts.
Snapshots and exact frames
The latest snapshot of a camera:
https://media.rhombussystems.com/media/{cameraUuid}/{mediaRegion}/snapshot.jpegcameraUuid and mediaRegion come from getMinimalCameraStateList.
curl "https://media.rhombussystems.com/media/CAMERA_UUID/MEDIA_REGION/snapshot.jpeg" \
-H "x-auth-scheme: api-token" \
-H "x-auth-apikey: $RHOMBUS_API_KEY" \
-o snapshot.jpegcurl "https://media.rhombussystems.com/media/CAMERA_UUID/MEDIA_REGION/snapshot.jpeg" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-o snapshot.jpeg<img src="https://media.rhombussystems.com/media/CAMERA_UUID/MEDIA_REGION/snapshot.jpeg?x-auth-scheme=federated-token&x-auth-ft=TOKEN" />For a frame at a specific time, call getExactFrameUri (timestamps in milliseconds). The URI it returns is not signed: fetch it with the same headers, or append the same query parameters in a browser.
Saved clip downloads
Saved clips (from spliceV3 or the console) are downloaded from the media host by region and clip UUID. Take the region from the clip's clipLocation:
curl "https://media.rhombussystems.com/media/metadata/{clipLocation.region}/{clipUuid}.mp4" \
-H "x-auth-scheme: api-token" \
-H "x-auth-apikey: $RHOMBUS_API_KEY" \
-o clip.mp4Use /media/play/{region}/{clipUuid}.mp4 instead to stream it inline (it supports Range requests). The credential's role needs permission to view saved video. A device-scoped federated token can fetch a saved clip only when every camera in the clip is the token's device.
Live and recorded video
POST /api/camera/getMediaUris returns the stream URLs for a camera:
| Field | Format | Use it with |
|---|---|---|
wanLiveM3u8Uri | HLS | Safari <video>, hls.js, AVPlayer, ExoPlayer, ffmpeg |
wanLiveMpdUri | DASH | dash.js |
wanLiveH264Uri | H.264 over WebSocket (lowest latency) | A custom decoder, or the React SDK |
wanVodM3u8UriTemplate, wanVodMpdUriTemplate | Recorded HLS / DASH | Replace {START_TIME} (Unix time, seconds) and {DURATION} (seconds) |
lan… fields | The same formats on your LAN | See Streaming over LAN |
Call getMediaUris with the credential your player will use, then authenticate each format as follows.
HLS
Add the token to the playlist URL. On the cloud hosts, the playlist carries the token's query parameters to every segment URL, so players need nothing else:
const url = `${wanLiveM3u8Uri}?x-auth-scheme=federated-token&x-auth-ft=${token}`;
if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = url; // Safari
} else {
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);
}From a native app or server, send the API key headers or Authorization: Bearer on every request instead.
DASH
Add the token to the manifest URL. On the cloud hosts, live and recorded DASH manifests (and alert clip manifests) carry the token's query parameters to every segment URL, so one URL works in any player:
const url = `${wanLiveMpdUri}?x-auth-scheme=federated-token&x-auth-ft=${encodeURIComponent(token)}`;
const player = dashjs.MediaPlayer().create();
player.initialize(video, url, true);Only federated-token and shared-link query parameters are carried to segments. API keys and x-auth-org are never copied into segment URLs, so a server or native client sends its headers on every request.
For streams from a camera on your LAN, add the token to every request yourself. With dash.js 5, a request interceptor does it, and leaves alone any URL that already carries the token:
const auth = `x-auth-scheme=federated-token&x-auth-ft=${encodeURIComponent(token)}`;
const withAuth = (url) => (url.includes("x-auth-ft=") ? url : `${url}${url.includes("?") ? "&" : "?"}${auth}`);
const player = dashjs.MediaPlayer().create();
player.addRequestInterceptor((request) => {
request.url = withAuth(request.url);
return Promise.resolve(request);
});
player.initialize(video, lanLiveMpdUris[0], true);WebSocket (H.264 and audio)
In a browser, put the token in the URL; from a server or native app, headers work too, as does an OAuth access token in Authorization: Bearer:
const ws = new WebSocket(`${wanLiveH264Uri}?x-auth-scheme=federated-token&x-auth-ft=${token}`);
ws.binaryType = "arraybuffer";A token minted with a domain works here: browsers send Origin on the WebSocket handshake. See The domain field.
Open media sockets (live video, talk, robot and PTZ) close with code 1008 within 60 seconds when their credential is revoked or its access changes. With an OAuth token, talk audio is checked per message, and sending audio without the physical scope closes the socket with 1008 (OAuth scopes).
The robot live socket accepts API keys, OAuth access tokens and federated tokens, the same way.
Partner keys
A partner calling a client organization's media from a server sends its partner credential as headers (x-auth-scheme: api-token with x-auth-apikey, or Authorization: Bearer with a partner OAuth token) plus x-auth-org: <client org UUID>. x-auth-org is a header only and is never copied into segment URLs. For a browser, mint a federated token for the client organization (send x-auth-org when you mint).
Shared stream links
createSharedLiveVideoStream creates a link to one camera's live video that works without any Rhombus credential:
sharedLiveVideoStreamUrlopens a Rhombus player page; embed it in an<iframe>.sharedLiveM3U8StreamUrlis a raw HLS playlist for your own player.
Anyone who has the link can watch, so treat it like a password:
expirationTimeSecsis the Unix time (in seconds) when the link stops working, not a duration. Leave it out and the link never expires.- Password-protected links (
createProtectedSharedLiveVideoStream): the player page asks for the password; for the raw HLS URL, sendAuthorization: Basic <base64(":" + password)>(an empty user name). A missing or wrong password returns 401 withWWW-Authenticate: Basic. - Delete the share (
deleteSharedLiveVideoStream) to stop it immediately.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
403 DOMAIN_NOT_ALLOWED on a media URL | The token was minted with a domain that isn't the page's exact origin (scheme, host, port), or the request carries no Origin or Referer (a server, a native player, a page with Referrer-Policy: ) | Mint with your page's exact origin, or without domain for clients outside a browser |
| 401 on a media URL | The credential is missing, damaged, expired or revoked; errorCode says which | Send the credential on every request, or mint a fresh token (Reading authentication errors) |
| A LAN DASH manifest loads, segments fail | Segment requests have no token | Add it to every request (see DASH) |
| Works on US hosts, fails on EU hosts | Keys and tokens are region-bound | Mint in the same region as the camera |
A device-scoped token fails on api2 | deviceUUid tokens only work for media | Use an unscoped token for REST calls |
| 403 with a device-scoped token on a clip or a shared link | The clip includes other cameras, or the URL names no device | Mint an unscoped token for those requests |
| 403 on a clip download | The role can't view saved video, or the region in the URL is wrong | Check the role and clipLocation |
A media WebSocket closes with code 1008 | The credential was revoked or its access changed, or an OAuth token without physical sent talk audio | Reconnect with a valid credential, or request the physical scope |
| A shared link stopped working | expirationTimeSecs was a duration (an already-past Unix time) | Send a Unix time in seconds |