Skip to main content
Live view streams a phone’s screen to a browser or app you build, over WebRTC. The API carries the signaling between your app and the phone. The video itself travels over the WebRTC connection.

Who can use it

A live view of a Lusor Phone can send taps and text over its own connection, outside the metering that direct control uses, so API keys cannot open one. To watch a Lusor Phone from your code, use GET …/screenshot or a capture. People signed in to the Lusor app can still watch any phone there.

Routes

A live view belongs to a session on the phone. Every route is under /v1/sessions/{id}/stream/, where {id} is the session id. A session without a phone returns 409 no_device.

Flow

1

Open the event stream

GET /v1/sessions/{id}/stream/events with your API key. Each event is named signal, and its data is one JSON message from the phone with an action: webrtc_offer, webrtc_ice, stream_ready, stream_error, stream_stopped or ice_update_applied. A keep-alive comment arrives every 15 seconds.
2

Get the ICE servers

GET /v1/sessions/{id}/stream/ice-servers. Pass data.iceServers to your RTCPeerConnection unchanged. If it is null, TURN is not available, so fall back to STUN.
3

Start the stream

POST /v1/sessions/{id}/stream/start. The phone answers on the event stream with a webrtc_offer.
4

Answer the offer

Set the offer as the remote description, create an answer, and send it with POST /v1/sessions/{id}/stream/signal and { "message": { "type": "webrtc_answer", … } }. Send each of your ICE candidates the same way with "type": "webrtc_ice". Apply the phone’s webrtc_ice events as they arrive.
5

Stop when done

POST /v1/sessions/{id}/stream/stop.
signal passes the rest of message to the phone unchanged. Any message.type other than webrtc_answer or webrtc_ice returns 400.

Several viewers

Pass the same viewer_id to start, connect, keepalive, request_frame and stop when several viewers watch one phone. It defaults to the session id.

Credential expiry

The TURN credential from ice-servers has an expiresAt time (Unix seconds). Renewing it during a live view is not available through the API yet. After expiresAt, start a new live view.