Appearance
Live screen monitoring: contract for integrators
Español: Monitoreo de pantalla en vivo.
A supervisor can watch an agent's screen live. This page is the integrator's view of that function: what has to be acknowledged, which calls and events are involved, who may watch, and what the network has to allow. The exact routes, bodies and status codes are in Recording transcripts, audio redaction and the screen-monitoring notice and in the generated API Reference; this page does not repeat them.
Availability
The dashboard side requires dashboard v0.51.1 or newer: earlier versions sent Permissions-Policy: display-capture=(), so the browser refused the screen capture. The core routes below are published from core v0.106.0. Live monitoring depends on the tenant flag screen_monitor_enabled and on the network path described under "Network requirement".
Field names, enums and status codes come from the vendored openapi/dialer-v1.yaml (info.version: 0.106.0).
What is involved
| Piece | Role |
|---|---|
Tenant flag screen_monitor_enabled | Off: every screen-monitor route answers 404 not_found, except the two ledger reads (GET /v1/screen-monitor/sessions and GET /v1/screen-monitor/acks) and closing a session. |
| Notice and acknowledgement | The agent's seat acknowledges a versioned notice with scope live_monitoring (and a separate acknowledgement for screen_recording). GET /v1/me reports it under screen_monitor.scopes.<scope>.ack_current; a scope is authorized only when enabled and ack_current are both true. |
| Arming | PUT /v1/screen-monitor/armed: the agent's dashboard says it holds a capture. Needs the seat checked in (409 not_checked_in) and a current live_monitoring acknowledgement (409 no_ack). One tab per seat. |
| Session | POST /v1/screen-monitor/sessions from a supervisor or admin seat; core validates, writes a ledger row and tells the agent. Core never carries the video: the two browsers negotiate it peer to peer through POST .../sessions/{id}/signal. |
| Ledger | GET /v1/screen-monitor/sessions: who watched whom, including the refused attempts. It never holds signalling payload. |
Who may watch
Only a ddu_ login whose seat template role is supervisor or admin. The role is always evaluated; it does not depend on supervision_role_enforced. A dd_ integration key is 403 forbidden (no person behind it) and a ddw_ widget token is refused before the handler. An agent can be watched only if it is armed and has a current live_monitoring acknowledgement, and by one supervisor at a time (409 agent_busy). Every outcome of a ddu_ login, refusals included, leaves a ledger row; the 403 of a dd_ key leaves none.
Events the browsers receive
Core tells each party on that party's private WebSocket topic (the spec names the frames; they are not listed in WebSocket events):
| Frame | Sent to | When |
|---|---|---|
screen.view_requested | the agent | a supervisor opened a session over the seat |
screen.disarmed | the agent's tab | another tab armed (reason: displaced) or the seat checked out (reason: checkout) |
screen.ended | the other party when a party closes it; both parties when the system closes it | the session closed, with result and reason (checkout, agent_stopped, peer_failed, credential_lapsed, flag_off, role, no_ack) |
A live session closes by itself when the agent checks out, either party loses its last socket, a credential lapses, or the flag, the supervisor's role or the agent's acknowledgement stop holding. A restart of core disarms every seat (fail-closed).
Network requirement
The video travels over WebRTC between the agent's browser and the supervisor's browser, not through core. Both browsers need a direct path or a relay.
- ICE in the dashboard. From dashboard
v0.52.0(dashboard#739) both parties ask core forGET /v1/screen-monitor/sessions/{id}/iceand add that TURN credential to the STUN list ofGET /v1/softphone/ice. If the cell has no relay (503 turn_unavailable) or the request fails, the session stays on STUN only, which fails behind a symmetric NAT; the dashboard then shows the session asfailed. Dashboardv0.51.1uses only the STUN list. Recording the screen does not use WebRTC (it uploads chunks over REST), so only live monitoring is affected. - TURN credential route: published in core
v0.107.0. Core signs an ephemeral credential per session and per party throughGET /v1/screen-monitor/sessions/{id}/ice(operationgetScreenMonitorSessionIce, core#2131, ADR #267): only the two parties of a session stillstartedget it (anyone else, an unknown id or a malformed id all answer the same404), the response isCache-Control: no-store,usernameis<unix expiry>:scr.<session_id>.<agent|viewer>, it expires one hour after it is issued (expires_at), and a cell with no TURN configured answers503 turn_unavailable(it never returns a list that looks valid and has no relay). The vendoredopenapi/dialer-v1.yamlis still0.106.0and does not list this route; the source isopenapi/dialer-v1.yamlof core at tagv0.107.0. Until the customer's cell has a relay configured, a customer behind a restrictive network should not expect live monitoring to connect. - Ports. A customer's firewall must let the browsers reach the cell's relay on 3478 (UDP and TCP) and 5349 (TCP) once the relay is enabled for their cell.
The agent's acknowledgement of the recording and supervision notice
Separate from the screen notice above, an agent can be asked to accept a recording and supervision notice before going available. It is a different notice (global, versioned, one text per language) with its own three operations in the API Reference:
| Operation | Route | What it does |
|---|---|---|
getAgentMonitoringNotice | GET /v1/agent-monitoring-notice | Returns the current version and its texts (title, body, text_sha256). 404 not_found while no version is published. A dd_ key or a ddu_ login may read it; a ddw_ widget token is 403. |
acknowledgeAgentMonitoringNotice | POST /v1/agent-monitoring-acknowledgements | The agent's own seat accepts notice_version (it must be the current one) read in lang. Only a ddu_ login with a linked seat: a dd_ key is 403 forbidden, a ddu_ with no active seat is 403 seat_not_linked, an agent_id that is not the caller's seat is 403 forbidden. 409 no_notice_published, 409 stale_notice, 422 when the current version has no text in lang. Accepting the same version again returns the first acceptance (200, no new row). |
getAgentMonitoringAcknowledgement | GET /v1/agents/{id}/monitoring-acknowledgement | Whether that agent has accepted the current version (monitoring.current) and its latest acceptance of any version (null if none). A seat reads its own; a supervisor or admin seat and a dd_ key read any agent of the tenant; another seat is 403 forbidden. |
The acceptances are an append-only ledger: they are never edited or deleted, and a newer notice version keeps the old acceptances on file, but they no longer cover the agent.
Flag off and flag on
The tenant flag agent_monitoring_ack_enforced is born off; only the founder turns it on, and this guide gives no date for it. The three operations above answer whether the flag is on or off.
- Flag off (the default). Nothing is asked of the agent: check-in and resume work as before. Acceptances already written are kept.
- Flag on, in a tenant with at least one campaign that records calls (
record_calls) and is not completed or archived. An agent who has not accepted the current version gets403 monitoring_ack_requiredfromPOST /v1/agents/checkinand fromPOST /v1/agents/{id}/resume, and nothing changes: the seat stays offline (or paused) and no SIP credential is issued. Core applies the same gate toPATCH /v1/agents/{id}/statuswithavailablefrompaused; closing after-call work is not gated. The error body carrieserror.notice_version, the version to accept. It isnullwhen no notice is published: the gate then stays closed, because there is nothing to accept.
To recover: read the notice (GET /v1/agent-monitoring-notice), show the agent the text, POST the acceptance from the agent's own seat with that notice_version and the lang shown, and repeat the check-in. The dashboard does this for the agent: the check-in card shows the notice as core delivers it, and the agent accepts it and the same check-in is retried.
Core publishes no legal text of its own: the notice catalogue is born empty. What the notice says and what each jurisdiction requires are not stated here; see the Roadmap.
Sources in core: lib/dialer/api/v1_router.ex (the three routes and monitoring_ack_required), lib/dialer/agents/monitoring_gate.ex (when the gate applies), lib/dialer/flags/catalog.ex (agent_monitoring_ack_enforced) and openapi/dialer-v1.yaml.
What this page does not claim
- The legal text of the notice and what each jurisdiction requires: see the Roadmap.
- Redaction of personal data in the screen recording (core#2017): not published in the spec.