Skip to main content
You can show a camera’s live feed in your user’s browser. Your backend creates a client session for the user. The browser then uses that client session to open a live view session and exchanges a WebRTC offer and answer with Seam. The video flows straight to the browser, and your servers never handle it.
Live video is enabled by default in production and sandbox workspaces during public beta. You don’t need to request access. The request and response shapes on this page may change before general availability.

Supported Providers

Check a camera’s can_stream_live_video capability flag before you offer live video for it. The flag is at the top level of the device object and is true only for cameras whose provider supports live video and that are currently online.

How It Works

All three /cameras/live_views endpoints accept only a client session token. Use your API key on your backend to create that client session, then send the returned client_session.token in the seam-client-session-token header for live view requests. Your API key stays on your backend. Authenticating a live view request directly with your workspace API key returns 401 Unauthorized. Follow Step 1 to create a client session scoped to the camera’s connected account.

Before You Begin

To stream live video, you need:
  • A production or sandbox workspace.
  • A connected account with a Tapo, Arlo, Reolink, or UniFi Protect camera whose can_stream_live_video is true.
  • A backend that can create client sessions with your API key.
  • A browser that supports WebRTC (RTCPeerConnection).

Step 1: Create a Client Session

On your backend, create a client session that has access to the camera’s connected account, and send its token to the browser. The live view can’t outlast the client session, so give the session a short lifetime.
Live video only works with client sessions created from connected_account_ids, connect_webview_ids, user_identifier_key, or user_identity_id. Client sessions created with customer_key can’t open a live view.
Request:
Response:
The response includes more client session fields. See the client session object. Before you offer live video for a camera, get the device and check that can_stream_live_video is true:
The flag is false when the provider does not support live video or the camera is offline. For local cameras, the paired Seam Bridge must also be online. Check the flag again right before opening a live view. A true flag describes device support and reported connectivity. Creating a session also depends on client-session authorization, workspace availability, and live-view capacity. Handle the live view errors even when the flag is true.

Step 2: Create a Live View Session

In the browser, call /cameras/live_views/create with the client session token in the seam-client-session-token header. A live view session reserves the camera for this viewer. Request:
Response:
  • The session ends at expires_at, the earliest of the requested duration, the provider’s session limit, or the client session’s expiration.
  • The response’s token authorizes the offer and stop calls for this session only. Keep it in memory, and don’t log it.
  • Seam sends Cache-Control: no-store on all live view responses.
  • See the camera live view objects for the complete camera_live_view_session and camera_live_view_answer reference.

Step 3: Send a WebRTC Offer

Create an RTCPeerConnection with a STUN server, add a receive-only video transceiver, and create an offer. Wait for ICE gathering to finish, then send the local description’s SDP to /cameras/live_views/offer, along with the session ID and token. Include the STUN server so you can use the same configuration for every camera.
JavaScript
Response:
camera_live_view_answer.sdp_answer is Seam’s WebRTC SDP answer, at most 64 KiB. Seam connects to the camera while handling this request, so the response can take several seconds.
  • One offer per session. Once you send an offer, you can’t send another one on the same session. To reconnect, create a new live view session.
  • Send the offer promptly. If the stream hasn’t started within 30 seconds of creating the session, Seam closes the session.
  • Match audio to include_audio. If include_audio is false, the offer must not contain an audio section, so add only a video transceiver. If it’s true, also call peerConnection.addTransceiver('audio', { direction: 'recvonly' }).
  • No ICE trickling. Send one offer to /cameras/live_views/offer and apply the returned answer. There’s no endpoint for sending more ICE candidates, so wait for ICE gathering to finish (or a few seconds) and send peerConnection.localDescription.sdp. The SDP returned by createOffer() doesn’t include any candidates.
  • Size limit. sdp_offer can be at most 64 KiB.

Step 4: Play the Stream

Apply the answer, then attach the incoming track to a <video> element.
JavaScript
Set ontrack before calling setRemoteDescription. Add autoplay, muted, and playsinline to the <video> element so that browsers start playback without a click. Unmute after a user gesture if you requested audio.

Step 5: Stop the Stream

When the user closes the viewer, close the peer connection and call /cameras/live_views/stop. This frees the camera for the next viewer right away. Always call /stop; closing the peer connection alone may leave the camera reserved until expires_at.
JavaScript
keepalive: true lets the request finish when it’s sent from a pagehide handler. Stopping a session that has already ended succeeds and returns {}.

Limits and Session Lifetime

Seam keeps checking that the viewer still has access while the stream is open. A live view ends when any of the following happens:
  • The session reaches expires_at.
  • You call /cameras/live_views/stop.
  • The client session expires or is revoked, or it loses access to the camera.
  • The camera is removed from Seam, or its connected account is deleted.
To keep watching past expires_at, stop the current session or wait for it to end, then create a new live view session and a new peer connection. Watch peerConnection.connectionState for failed or closed to detect the end of a stream.

Errors

For 409 and 503, wait a few seconds and create a new live view session. Retry with backoff, and stop after a few attempts.

Complete Example

This example serves a web page that plays one camera’s live feed. It uses Node.js 18 or later and the seam package.
  1. Run npm install seam.
  2. Set SEAM_API_KEY, SEAM_CONNECTED_ACCOUNT_ID, and SEAM_DEVICE_ID to your API key, the camera’s connected account, and the camera’s device ID.
  3. Save both files in the same directory and run node server.mjs.
  4. Open http://localhost:3000 and click Start.
In production, authenticate your user before creating a client session, and only include the connected accounts that belong to that user.
JavaScript
HTML

Next Steps