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 in beta. Seam turns on camera events, event media, and live video for each workspace separately, so contact Seam and ask for live video to be turned on for yours. 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 true only for cameras whose provider supports live video and that are currently online. Ring cameras and doorbells report false.

How It Works

All three /cameras/live_views endpoints accept only a client session token. Your API key never reaches the browser.

Before You Begin

To stream live video, you need:
  • A workspace with live video turned on.
  • A connected account with a Tapo or Arlo 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 while the camera is offline, so check it again right before you open a live view.

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: duration_seconds after creation, or when the client session expires, whichever is sooner.
  • 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, add a receive-only video transceiver, and create an offer. Send the offer’s SDP to /cameras/live_views/offer, along with the session ID and token.
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. Seam ignores the ICE candidates in your offer and puts its own candidates in the answer. You don’t need to wait for ICE gathering, and there’s no endpoint for sending more 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. Otherwise, Seam frees it when it notices the connection has closed or when the session expires.
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 browser closes the peer connection.
  • 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, create a new live view session and connect a new peer connection before the old one ends. 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