> ## Documentation index
> Fetch the complete documentation index at: https://developer.rhombus.com/llms.txt
> Use it to discover all available pages before exploring further.

# Show Cameras in Your App

> Put Rhombus camera thumbnails and live video in your own web or mobile app: list cameras on your server, mint short-lived federated tokens for each viewer, load snapshots, and play live video with the React SDK or an HLS player.

This guide adds a camera wall to your application: a grid of current thumbnails, and live video when a user opens a camera. Your API key never leaves your server; the browser or app gets short-lived [federated tokens](https://developer.rhombus.com/authentication/federated-tokens) limited to the cameras you choose.

```mermaid
sequenceDiagram
    participant U as Your app (browser or mobile)
    participant B as Your backend
    participant R as Rhombus
    U->>B: List my cameras (your own auth)
    B->>R: getMinimalCameraStateList (API key)
    B-->>U: Cameras this user may see
    U->>B: Token for camera X
    B->>R: generateFederatedSessionToken (deviceUUid = X)
    B-->>U: Short-lived token
    U->>R: Snapshot and live video with the token
```

## 1. List cameras on your server

Call [`getMinimalCameraStateList`](https://developer.rhombus.com/api-reference/camera-webservice/get-minimal-camera-state-list) with your API key. Keep `uuid`, `name`, `locationUuid`, `mediaRegion` and `connectionStatus` for each camera, then **filter the list by your own rules** before you return it: your users aren't Rhombus users, so only your backend knows which cameras each of them may see.

Use a key whose role covers only the locations your app serves, with live-only or read-only device access; every token you mint inherits it ([Permissions](https://developer.rhombus.com/authentication/permissions#least-privilege)).

## 2. Mint a token per viewer and camera

When a user opens a camera, your backend checks that they may see it and mints a token scoped to that camera with [`generateFederatedSessionToken`](https://developer.rhombus.com/api-reference/org-webservice/generate-federated-session-token):

```javascript Node.js (Express)
// Your web app's exact origin. Leave domain out for native apps (see below).
const APP_ORIGIN = "https://app.example.com";

app.post("/api/cameras/:uuid/token", requireUser, async (req, res) => {
  if (!userCanSeeCamera(req.user, req.params.uuid)) return res.sendStatus(403);
  const r = await fetch("https://api2.rhombussystems.com/api/org/generateFederatedSessionToken", {
    method: "POST",
    headers: {
      "x-auth-scheme": "api-token",
      "x-auth-apikey": process.env.RHOMBUS_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ durationSec: 900, deviceUUid: req.params.uuid, domain: APP_ORIGIN }),
  });
  if (!r.ok) return res.sendStatus(502);
  const { federatedSessionToken, expiresAtMs } = await r.json();
  // Remember the token so it can be revoked when the user signs out (step 5)
  (req.session.rhombusTokens ??= []).push(federatedSessionToken);
  res.json({ token: federatedSessionToken, expiresAtMs });
});
```

Keep tokens short (minutes, not days; never more than 24 hours), scoped with `deviceUUid`, and renew them before the `expiresAtMs` in the mint response.

For a web app, set `domain` to your app's exact origin, an absolute origin such as `https://app.example.com`. Rhombus then accepts the token only on requests whose `Origin` (or, when there's none, `Referer`) is that origin, and answers any other with 403 `DOMAIN_NOT_ALLOWED`, so a token copied out of your page doesn't work elsewhere. Image requests carry no `Origin`, so they rely on the `Referer`: don't set `Referrer-Policy: no-referrer` on pages that show camera images. Leave `domain` out for native mobile apps, which send neither header. [The domain field](https://developer.rhombus.com/authentication/federated-tokens#the-domain-field) has the details.

## 3. Show thumbnails

A camera's latest snapshot is a plain image URL, so an `<img>` tag can load it with the token in the query string:

```html
<img
  alt="Loading dock"
  src="https://media.rhombussystems.com/media/CAMERA_UUID/MEDIA_REGION/snapshot.jpeg?x-auth-scheme=federated-token&x-auth-ft=TOKEN"
/>
```

`MEDIA_REGION` is the camera's `mediaRegion` from step 1. Refresh thumbnails every few seconds at most, and only for cameras on screen. A grid of many cameras can mint one token per camera, or a single token without `deviceUUid` that covers every camera the key's role can see; the per-camera token limits what a leaked token exposes.

## 4. Play live video

Pick the player that fits your app:

| App                   | Player                                                                                                        | Guide                                                                                           |
| --------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| React web app         | The Rhombus React SDK: give it a function that fetches a token from your backend, and it renews tokens itself | [React SDK](https://developer.rhombus.com/implementations/react-sdk)                            |
| Other web app         | hls.js, or Safari's native player, on `wanLiveM3u8Uri` with the token appended                                | [Media and streams: HLS](https://developer.rhombus.com/authentication/media#hls)                |
| Native iOS or Android | AVPlayer or ExoPlayer on the same HLS URL                                                                     | [Media and streams](https://developer.rhombus.com/authentication/media#live-and-recorded-video) |

Your backend gets the stream URLs from [`getMediaUris`](https://developer.rhombus.com/api-reference/camera-webservice/get-camera-media-uris) for the camera and passes them to the app along with the token. Recorded video uses the same URLs with a start time and duration; see [Streaming video](https://developer.rhombus.com/implementations/streaming-video).

## 5. Revoke tokens when the user signs out

A token stops working at its `expiresAtMs`, or sooner if you revoke it or delete the API key that minted it. When a user signs out of your app, revoke the tokens you minted for their session with `POST /api/org/revokeFederatedSessionToken`. It takes effect within 60 seconds, an open live-video socket that uses the token closes with code `1008`, and the call returns 200 whether or not the token still existed:

```javascript Node.js (Express)
app.post("/logout", requireUser, async (req, res) => {
  for (const token of req.session.rhombusTokens ?? []) {
    await fetch("https://api2.rhombussystems.com/api/org/revokeFederatedSessionToken", {
      method: "POST",
      headers: {
        "x-auth-scheme": "api-token",
        "x-auth-apikey": process.env.RHOMBUS_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ federatedSessionToken: token }),
    });
  }
  req.session.destroy(() => res.sendStatus(204));
});
```

The app can also revoke its own token with the same call, authenticated with that token (`x-auth-scheme=federated-token&x-auth-ft=<token>`).

## Before you launch

- The API key stays on your server; nothing on a user's device can mint tokens.
- Your backend checks, for every token request, that the user may see that camera.
- Tokens are scoped to one camera, live for minutes, and are bound to your web app's origin with `domain`.
- Signing out revokes the tokens minted for that session.
- Token-bearing URLs are kept out of logs, analytics and error reports.
- Your app fetches a new token before `expiresAtMs`, and handles a 401 by fetching a new one and reloading the image or stream.
