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 sameviewer_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 fromice-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.
