Skip to main content

Documentation Index

Fetch the complete documentation index at: https://api-docs.rhombus.community/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Rhombus cameras expose multiple ways to access video content through the public API:
  • Live streaming for real-time monitoring, embedded as a Rhombus-hosted iframe
  • HLS manifests (.m3u8) for direct playback in your own player
  • Thumbnail images for dashboards and quick previews
  • Frame captures at any historical timestamp
For downloading recorded footage as MP4 files, see Recorded Video Access.

Getting Camera Thumbnails

All Rhombus cameras automatically upload thumbnail images. These are perfect for dashboards, monitoring interfaces, and quick status checks.

Thumbnail Endpoint

Retrieve camera thumbnails using a GET request to:
https://media.rhombussystems.com/media/{cameraUuid}/{mediaRegion}/snapshot.jpeg
All media.rhombussystems.com endpoints use the same authentication as the main API — include the x-auth-scheme and x-auth-apikey headers on every request.
To get the cameraUuid and mediaRegion for a camera, call POST /api/camera/getMinimalCameraStateList. The response contains a cameraStates array; each entry has uuid and mediaRegion fields.

Basic Implementation

cURL
# 1. List cameras to get UUIDs and media regions
curl -X POST \
  "https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{}'
cURL
# 2. Fetch the thumbnail using a camera's UUID and media region
curl -X GET \
  "https://media.rhombussystems.com/media/{CAMERA_UUID}/{MEDIA_REGION}/snapshot.jpeg" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  --output thumbnail.jpg

Getting Frames at Specific Times

To get a JPEG image at a specific historical timestamp, call POST /api/video/getExactFrameUri. The response contains a frameUri — make a GET request to that URI with the same authentication headers to download the frame.
async function getFrameAtTime(cameraUuid, timestampMs, apiKey) {
  // Get a signed frame URI
  const frameUriResponse = await fetch('https://api2.rhombussystems.com/api/video/getExactFrameUri', {
    method: 'POST',
    headers: {
      'x-auth-scheme': 'api-token',
      'x-auth-apikey': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      cameraUuid: cameraUuid,
      timestampMs: timestampMs // epoch milliseconds
    })
  });

  const { frameUri } = await frameUriResponse.json();

  // Fetch the actual JPEG
  const frameResponse = await fetch(frameUri, {
    headers: {
      'x-auth-scheme': 'api-token',
      'x-auth-apikey': apiKey
    }
  });

  return frameResponse.blob();
}
The request also accepts optional downscaleFactor, jpgQuality, and permyriadCropX/permyriadCropY/permyriadCropWidth/permyriadCropHeight fields (each in 1/10000ths of the image dimensions, where 10000 = 100%).

Live Video Streaming

Rhombus offers two ways to play live video in your application:
  1. Embed the Rhombus player as an iframe — easiest path; analytics and timeline events render automatically.
  2. Play the HLS manifest directly — use your own player (hls.js, Safari native, etc.) for full UI control.
Both come from the same API call: POST /api/camera/createSharedLiveVideoStream. The response contains both URLs:
FieldDescription
sharedLiveVideoStreamUrlHosted Rhombus player URL — embed in an iframe
sharedLiveM3U8StreamUrlHLS (.m3u8) manifest — play directly with any HLS player
sharedLiveVideoStreamUuidUUID of the created share, useful for revoking later
Create a shared stream, then drop the returned URL into an iframe. The iframe player handles authentication via the URL itself, so no API key is needed in the browser.
async function createSharedStream(cameraUuid, apiKey) {
  const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
    method: 'POST',
    headers: {
      'x-auth-scheme': 'api-token',
      'x-auth-apikey': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ cameraUuid })
  });

  const data = await response.json();
  return data.sharedLiveVideoStreamUrl;
}
Embed the returned URL:
<iframe
  allow="fullscreen"
  frameborder="0"
  height="100%"
  width="100%"
  src="SHARED_STREAM_URL_GOES_HERE">
</iframe>

React Component Example

import React, { useEffect, useState } from 'react';

const RhombusSharedStream = ({
  cameraUuid,
  apiKey,
  width = "100%",
  height = "400px",
  options = {}
}) => {
  const [streamUrl, setStreamUrl] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;

    (async () => {
      try {
        setLoading(true);
        const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
          method: 'POST',
          headers: {
            'x-auth-scheme': 'api-token',
            'x-auth-apikey': apiKey,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ cameraUuid })
        });

        if (!response.ok) throw new Error('Failed to create shared stream');
        const data = await response.json();

        const url = new URL(data.sharedLiveVideoStreamUrl);
        Object.entries(options).forEach(([key, value]) => {
          url.searchParams.set(key, value);
        });

        if (!cancelled) {
          setStreamUrl(url.toString());
          setError(null);
        }
      } catch (err) {
        if (!cancelled) setError(err.message);
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();

    return () => { cancelled = true; };
  }, [cameraUuid, apiKey, options]);

  if (loading) return <div>Loading stream...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <iframe
      src={streamUrl}
      width={width}
      height={height}
      frameBorder="0"
      allow="fullscreen"
      title={`Camera ${cameraUuid} Stream`}
    />
  );
};

export default RhombusSharedStream;

URL Parameters for Enhanced Control

Customize the embedded player with URL parameters. All parameters can be set to true or false:
ParameterDescription
disableautoplayEnable or disable video autoplay upon loading
hideeventsEnable or disable the timeline and related events
realtimeEnable or disable real-time streaming by default
showheaderEnable or disable zoom & streaming buttons at the top
showfooterEnable or disable camera name and timestamp at bottom
lowResForce low resolution on the video stream
performanceModeForce performance mode allowing GPU usage
tTimestamp (epoch milliseconds) where video should start

Parameter Usage

Append parameters to the shared stream URL: https://{{url}}/?{{variable}}=true&{{variable}}=false Example:
https://console.rhombussystems.com/share/live/ehrF58ABSj2VT8QzXh7GVA?disableautoplay=true&hideevents=true&realtime=true

React Usage with Parameters

<RhombusSharedStream
  cameraUuid="your-camera-uuid"
  apiKey="your-api-key"
  options={{
    disableautoplay: 'true',
    hideevents: 'true',
    realtime: 'true',
    showheader: 'false',
    lowRes: 'false'
  }}
/>

Embedding HLS Streams Directly

If you want to render live video in your own player rather than the hosted iframe, use sharedLiveM3U8StreamUrl from the same createSharedLiveVideoStream response. The manifest is a standard HLS .m3u8 and is signed with a token in the URL itself — no x-auth-apikey header is needed when fetching it.
The HLS URL is single-org-scoped and time-limited. Treat it as a credential and don’t expose it in public-facing pages without an expiration policy.
Safari plays HLS natively. For other browsers, use hls.js:
<video id="rhombus-live" controls autoplay muted style="width: 100%;"></video>

<script type="module">
  import Hls from 'https://cdn.jsdelivr.net/npm/hls.js@latest/dist/hls.min.js';

  async function startLive(cameraUuid, apiKey) {
    const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
      method: 'POST',
      headers: {
        'x-auth-scheme': 'api-token',
        'x-auth-apikey': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ cameraUuid })
    });

    const { sharedLiveM3U8StreamUrl } = await response.json();
    const video = document.getElementById('rhombus-live');

    if (Hls.isSupported()) {
      const hls = new Hls();
      hls.loadSource(sharedLiveM3U8StreamUrl);
      hls.attachMedia(video);
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = sharedLiveM3U8StreamUrl;
    }
  }

  startLive('YOUR_CAMERA_UUID', 'YOUR_API_KEY');
</script>
Because creating a shared stream requires an API key, do the createSharedLiveVideoStream call from a server you control and pass only the resulting sharedLiveM3U8StreamUrl (or sharedLiveVideoStreamUrl) to the browser. Keep the API key out of client code.

Recorded Video Access

Downloading recorded footage as a single MP4 file is not exposed as a public API operation. The Rhombus storage layer serves recorded video as a sequence of media segments rather than a pre-rendered MP4, so producing a downloadable clip requires fetching and concatenating segments client-side. The supported way to do this is the Rhombus CLI:
# Download the last hour of footage from a camera as MP4
rhombus footage --camera CAMERA_UUID --duration 1h --output footage.mp4

# Download the clip associated with a specific alert
rhombus alert download --alert ALERT_UUID --output alert.mp4
For alert-driven workflows, configure a webhook to deliver alert events to your service, then call the CLI (or replicate its segment-fetch logic) to retrieve the corresponding clip. To list recent alerts programmatically, use POST /api/event/getPolicyAlerts — the previous getPolicyAlertsV2 endpoint is deprecated.

Complete Implementation Example

A React component supporting both thumbnail and live-stream view types:
import React, { useEffect, useRef, useState, useCallback } from 'react';

const RhombusCameraViewer = ({
  cameraUuid,
  apiKey,
  viewType = 'thumbnail', // 'thumbnail' | 'stream'
  autoRefresh = true,
  refreshInterval = 5000,
  streamOptions = {},
  width = "100%",
  height = "400px"
}) => {
  const [thumbnailUrl, setThumbnailUrl] = useState(null);
  const [streamUrl, setStreamUrl] = useState(null);
  const [cameraDetails, setCameraDetails] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const lastObjectUrl = useRef(null);

  const getCameraDetails = useCallback(async () => {
    const response = await fetch('https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList', {
      method: 'POST',
      headers: {
        'x-auth-scheme': 'api-token',
        'x-auth-apikey': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({})
    });

    if (!response.ok) throw new Error('Failed to list cameras');
    const data = await response.json();
    const camera = (data.cameraStates || []).find(c => c.uuid === cameraUuid);
    if (!camera) throw new Error('Camera not found');
    return camera;
  }, [cameraUuid, apiKey]);

  const loadThumbnail = useCallback(async (camera) => {
    const response = await fetch(
      `https://media.rhombussystems.com/media/${camera.uuid}/${camera.mediaRegion}/snapshot.jpeg`,
      { headers: { 'x-auth-scheme': 'api-token', 'x-auth-apikey': apiKey } }
    );
    if (!response.ok) throw new Error('Failed to load thumbnail');

    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    if (lastObjectUrl.current) URL.revokeObjectURL(lastObjectUrl.current);
    lastObjectUrl.current = url;
    setThumbnailUrl(url);
  }, [apiKey]);

  const createSharedStream = useCallback(async () => {
    const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
      method: 'POST',
      headers: {
        'x-auth-scheme': 'api-token',
        'x-auth-apikey': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ cameraUuid })
    });
    if (!response.ok) throw new Error('Failed to create shared stream');

    const data = await response.json();
    const url = new URL(data.sharedLiveVideoStreamUrl);
    Object.entries(streamOptions).forEach(([key, value]) => {
      url.searchParams.set(key, value);
    });
    setStreamUrl(url.toString());
  }, [cameraUuid, apiKey, streamOptions]);

  useEffect(() => {
    let cancelled = false;
    let interval = null;

    (async () => {
      try {
        setIsLoading(true);
        const camera = await getCameraDetails();
        if (cancelled) return;
        setCameraDetails(camera);

        if (viewType === 'thumbnail') {
          await loadThumbnail(camera);
          if (autoRefresh) {
            interval = setInterval(() => {
              loadThumbnail(camera).catch(err => setError(err.message));
            }, refreshInterval);
          }
        } else if (viewType === 'stream') {
          await createSharedStream();
        }

        setError(null);
      } catch (err) {
        if (!cancelled) setError(err.message);
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    })();

    return () => {
      cancelled = true;
      if (interval) clearInterval(interval);
      if (lastObjectUrl.current) {
        URL.revokeObjectURL(lastObjectUrl.current);
        lastObjectUrl.current = null;
      }
    };
  }, [viewType, autoRefresh, refreshInterval, getCameraDetails, loadThumbnail, createSharedStream]);

  if (error) {
    return (
      <div style={{ padding: '20px', border: '1px solid #ff6b6b', borderRadius: '4px' }}>
        <p>Error: {error}</p>
      </div>
    );
  }

  if (isLoading) {
    return <div style={{ padding: '20px', textAlign: 'center' }}>Loading camera...</div>;
  }

  return (
    <div style={{ width, height }}>
      {viewType === 'thumbnail' && thumbnailUrl && (
        <img
          src={thumbnailUrl}
          alt={cameraDetails?.name || `Camera ${cameraUuid}`}
          style={{ width: '100%', height: '100%', objectFit: 'cover' }}
        />
      )}
      {viewType === 'stream' && streamUrl && (
        <iframe
          src={streamUrl}
          width="100%"
          height="100%"
          frameBorder="0"
          allow="fullscreen"
          title={`Camera ${cameraUuid} Stream`}
        />
      )}
    </div>
  );
};

export default RhombusCameraViewer;

Usage Examples

// Thumbnail view with auto-refresh
<RhombusCameraViewer
  cameraUuid="your-camera-uuid"
  apiKey="your-api-key"
  viewType="thumbnail"
  autoRefresh={true}
  refreshInterval={3000}
  width="640px"
  height="480px"
/>

// Live stream with custom options
<RhombusCameraViewer
  cameraUuid="your-camera-uuid"
  apiKey="your-api-key"
  viewType="stream"
  streamOptions={{
    disableautoplay: 'false',
    hideevents: 'false',
    realtime: 'true',
    showheader: 'true',
    showfooter: 'true'
  }}
  width="100%"
  height="600px"
/>

Best Practices

Performance Optimization

  • Thumbnail caching: Cache thumbnails client-side and refresh on a sensible interval rather than polling continuously.
  • Lazy loading: Load video content only when needed or when in viewport.
  • Quality selection: Use lowRes=true on the iframe player for grids and dashboards.

Security Considerations

  • Keep API keys server-side: Never expose x-auth-apikey in browser code. Call createSharedLiveVideoStream from your backend and pass only the resulting URLs to the client.
  • HTTPS only: Always use HTTPS for API calls and shared stream URLs.
  • Scope keys narrowly: Limit each API key to the minimum permissions required.
  • Set expirations on shared streams: Pass expirationTimeSecs when creating shared streams so URLs don’t outlive the use case.

Error Handling

class RhombusVideoError extends Error {
  constructor(message, code, cameraUuid) {
    super(message);
    this.name = 'RhombusVideoError';
    this.code = code;
    this.cameraUuid = cameraUuid;
  }
}

async function fetchWithRetry(url, init, retries = 3) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 10000);
    try {
      const response = await fetch(url, { ...init, signal: controller.signal });
      clearTimeout(timer);
      if (!response.ok) {
        throw new RhombusVideoError(`HTTP ${response.status}`, response.status, init?.cameraUuid);
      }
      return response;
    } catch (error) {
      clearTimeout(timer);
      if (attempt === retries) throw error;
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
    }
  }
}

Accessibility

<video
  controls
  aria-label={`Live feed from camera ${cameraName}`}
  poster={thumbnailUrl}
>
  <track kind="captions" src="captions.vtt" srcLang="en" label="English" />
  Your browser does not support the video tag.
</video>

Troubleshooting

Thumbnail not loading
  • Verify cameraUuid and mediaRegion came from a recent getMinimalCameraStateList response — mediaRegion can change.
  • Check that the API key has access to the camera’s location.
  • Confirm the camera is online (connectionStatus in the camera state response).
Stream connection failures
  • Check network connectivity to *.rhombussystems.com.
  • For HLS playback, confirm the browser supports HLS natively or that hls.js is loaded.
  • Inspect firewall/proxy rules — corporate proxies frequently block media segment requests.
Poor video quality
  • Pass lowRes=true for bandwidth-constrained clients.
  • Check upstream bandwidth at the camera site.

Next Steps

Video Player

Build a custom video player with HLS playback and timeline controls.

LAN Realtime Detection

Parse AI bounding boxes from the LAN H.264 WebSocket stream and overlay them on live video.

Backup Footage

Download and archive camera footage to local storage.
Last modified on May 7, 2026