Update a host's console-mode config (partial; pushes config_update to the agent).
const url = 'http://localhost:8080/v1/admin/hosts/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/console-config';const options = { method: 'PATCH', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"enabled":true,"connector":"example","output_id":"example","mode":{"width":1,"height":1,"refresh_millihz":1},"compositor":"weston","audio_output":"example","stream":true,"stream_audio":true,"input_devices":"auto","grab":true,"auto_start_on_display":true,"auto_connect_controller":true,"default_app":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","default_user":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","fullscreen":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}use std::str::FromStr;use serde_json::json;use reqwest;
#[tokio::main]pub async fn main() { let url = "http://localhost:8080/v1/admin/hosts/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/console-config";
let payload = json!({ "enabled": true, "connector": "example", "output_id": "example", "mode": json!({ "width": 1, "height": 1, "refresh_millihz": 1 }), "compositor": "weston", "audio_output": "example", "stream": true, "stream_audio": true, "input_devices": "auto", "grab": true, "auto_start_on_display": true, "auto_connect_controller": true, "default_app": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "default_user": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "fullscreen": true });
let mut headers = reqwest::header::HeaderMap::new(); headers.insert("Authorization", "Bearer <token>".parse().unwrap()); headers.insert("Content-Type", "application/json".parse().unwrap());
let client = reqwest::Client::new(); let response = client.request(reqwest::Method::from_str("PATCH").unwrap(), url) .headers(headers) .json(&payload) .send() .await;
let results = response.unwrap() .json::<serde_json::Value>() .await .unwrap();
dbg!(results);}curl --request PATCH \ --url http://localhost:8080/v1/admin/hosts/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/console-config \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "enabled": true, "connector": "example", "output_id": "example", "mode": { "width": 1, "height": 1, "refresh_millihz": 1 }, "compositor": "weston", "audio_output": "example", "stream": true, "stream_audio": true, "input_devices": "auto", "grab": true, "auto_start_on_display": true, "auto_connect_controller": true, "default_app": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "default_user": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "fullscreen": true }'Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Request Bodyrequired
Section titled “Request Bodyrequired”Per-host console-mode config. On PATCH the body is partial (any subset); on GET it is the resolved object. Off + local-only by default; audio_output has no default (null = quiet).
object
‘auto’ or a DRM connector name (DP-4, HDMI-A-1, …).
Card-scoped DRM output id from capabilities.outputs; null selects automatic output.
Local host sink id (‘auto’ | alsa hw id | hdmi | motherboard | usb:*), or null = no local audio.
Also stream over WebRTC (dual-output). Default false = local-only.
CM-06: owner of auto-started console sessions (users.id). Required when auto_start_on_display=true.
Responses
Section titled “Responses”New effective state.
object
Per-host console-mode config. On PATCH the body is partial (any subset); on GET it is the resolved object. Off + local-only by default; audio_output has no default (null = quiet).
object
‘auto’ or a DRM connector name (DP-4, HDMI-A-1, …).
Card-scoped DRM output id from capabilities.outputs; null selects automatic output.
Local host sink id (‘auto’ | alsa hw id | hdmi | motherboard | usb:*), or null = no local audio.
Also stream over WebRTC (dual-output). Default false = local-only.
CM-06: owner of auto-started console sessions (users.id). Required when auto_start_on_display=true.
What the host can do in console mode (from agent-api capacity.console_capabilities). Empty arrays if unreported.
object
Typed per-card DRM connector and mode inventory. Additive; absent from older agents.
object
Stable card-scoped output id, e.g. card1:DP-4.
object
Millihertz derived from exact DRM timing totals.
object
object
Example
{ "config": { "compositor": "weston", "input_devices": "auto" }}Malformed or invalid request.
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}Missing/invalid/expired/revoked token.
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}Authenticated but insufficient role / not the owner (precedes resource lookup).
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}No such resource.
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}