Skip to content

Launch-profile eligibility + recommendation for the caller's device.

GET
/v1/me/profiles
curl --request GET \
--url http://localhost:8080/v1/me/profiles \
--header 'Authorization: Bearer <token>'

UI-P5 adds the OPTIONAL app_id query parameter. With it, the response is narrowed to the launch profiles that app offers (its allow-list, intersected with eligibility), so a client renders the correct per-app launch menu without intersecting anything itself. Without it the response is exactly the pre-UI-P5 one.

THIS FILTER IS A CONVENIENCE, NOT THE GATE. POST /v1/sessions enforces the same allow-list independently, so a client that omits the parameter, or ignores what it returns, cannot launch anything extra.

app_id
string format: uuid

UI-P5, OPTIONAL. Narrow the evaluation to the launch profiles this app offers. 404 when the app does not resolve under the SAME visibility rule as GET /v1/apps/{id} (non-admin: absent or disabled) - never a silent fall-back to the full catalogue, which would widen the menu on a typo.

OK.

Media typeapplication/json

GET /v1/me/profiles response: eligibility + recommendation. BREAKING (UI-P4) - profiles[] now holds LAUNCH PROFILES, which have no single top-level resolution. A consumer reading profiles[].width breaks; quasar-client (ffi_control.rs, ffi.rs) hand-projects width/height/fps off this shape and needs a re-pin, deliberately deferred to its own work.

object
recommended_id
required

UI-P4: a LAUNCH PROFILE id.

string
confidence
required
string
Allowed values: high low
notes
required
Array<object>

A stable reason code + message explaining an eligibility decision (AS10-02).

object
code
required
string
message
required
string
profiles
required
Array<object>

UI-P4: a launch profile with its eligibility verdict and its per-rung verdicts. A launch profile is eligible if ANY rung is - which INVERTS the pre-UI-P4 semantics on purpose, so a 4K launch profile no longer vanishes for a client that cannot decode 4K; it is offered and resolves to its H.264 floor rung. Counterweight: a launch profile whose TOP rung is ineligible while a lower one is not is classified risky, NOT eligible, so it can never become recommended_id - without that rule the inverted semantics would silently recommend a “4K” profile that always streams 1080p.

object
id
required
string
display_name
required
string
description
required
string
nominal
required

UI-P4: the TOP rung’s numbers, echoed so a picker and the admin app-editor preview have something to render. ADVERTISED, NOT RESOLVED - if a rung falls through, the session streams something else, and the session’s own stream block is the truth. Clients must not treat this as a promise.

object
width
required
integer
height
required
integer
fps
required
integer
bitrate_kbps
required
integer
eligibility
required
string
Allowed values: eligible risky ineligible
reasons
required
Array<object>

A stable reason code + message explaining an eligibility decision (AS10-02).

object
code
required
string
message
required
string
rungs
required
Array
>= 1 items
object
id
required

Rungs created by migration 0036’s fan-out use ‘-’, e.g. ‘1080p60-h264’.

string
display_name
required
string
codec
required

The CATALOG codec vocabulary used by stream profiles (rungs). Note ‘hevc’, not the wire ‘h265’ - the rename is bridged in exactly one place server-side and never on this surface.

string
Allowed values: h264 hevc av1
width
required
integer
height
required
integer
fps
required
integer
h264_profile
required

Meaningful only when codec is h264. The browser (WebRTC) receiver rejects High on both VA and NVENC, so a browser launch still negotiates down to constrained-baseline; this records the rung’s preference for a capable client.

string
nominal_bitrate_kbps
required
integer
min_offer_bandwidth_kbps
required
integer
recommended_offer_bandwidth_kbps
required
integer
headroom_factor
required
number
abr_floor_kbps
required
integer
max_startup_rtt_ms
required
integer
min_decode_height
required
integer
high_refresh_display
required
string
Allowed values: none recommended required
hardware_encoder_required
required
boolean
browser_client
required
string
Allowed values: recommended supported risky
playout0_ms
required
integer
warnings

Present only on a WRITE response (PATCH /v1/admin/stream-profiles/{id}), and omitted entirely when there is nothing to say, so every read path is byte-identical to before. Reuses UI-P4’s WriteWarning shape rather than inventing a second one. Emitted when a codec change leaves every launch profile listing this rung still carrying a launchable H.264 rung, but no longer LAST. That warns rather than rejects, because the 0036 fan-out preserved each profile’s stored codec order and H.264 is often first, so rejecting would make migrated profiles permanently uneditable. A change that would leave a chain with NO launchable H.264 rung is a 409 instead, naming the affected launch profiles: an operator editing one rung has no way to know which chains depend on it. This guard exists because the rule was previously enforced only when editing a launch profile’s rung LIST, so editing a stream profile’s codec could silently invalidate every chain listing it, with nothing surfacing until a launch failed at dispatch. See docs/design/plans/2026-07-29-library-ux-fixes-spec.md.

Array<object>

UI-P4: a non-fatal advisory returned on an admin profile write. A warning NEVER fails the request - it reports a configuration that is legal but almost certainly not what the operator meant.

object
code
required

H264_floor_not_last - every rung after the H.264 rung is unreachable, because H.264 passes every clamp. floor_not_least_demanding - the H.264 rung has a higher min_offer_bandwidth_kbps or min_decode_height than a rung above it, or requires a hardware encoder while a rung above it does not; a floor harder to satisfy than the rung above it is a misconfiguration.

string
Allowed values: h264_floor_not_last floor_not_least_demanding
message
required
string
visibility
required

Rungs created by the fan-out are ‘internal’: a rung is never offered standalone, only via the launch profile that lists it.

string
Allowed values: user debug internal
used_by

Admin read only: the launch profiles listing this rung. Shown inside the editor as well as the list, because editing a shared object changes every consumer.

Array<object>

A minimal reference to a profile object, used in ‘used by’ lists.

object
id
required
string
display_name
required
string
session_count

Admin read only, ADDITIVE, omitted when zero: how many session rows record this rung as the one they resolved to. This is the SECOND “used by” dimension and it also blocks DELETE - sessions.stream_profile_id is a plain foreign key with no ON DELETE clause (NO ACTION on purpose: ON DELETE SET NULL would erase which rung a historical session actually got, which is the entire reason the column exists), so one historical session refuses the delete at the database. A client must treat a non-zero session_count exactly like a non-empty used_by when deciding whether to offer Delete.

integer
position
required
integer
>= 1
eligibility
required
string
Allowed values: eligible risky ineligible
reasons
required
Array<object>

A stable reason code + message explaining an eligibility decision (AS10-02).

object
code
required
string
message
required
string
Example
{
"confidence": "high",
"profiles": [
{
"eligibility": "eligible",
"rungs": [
{
"codec": "h264",
"high_refresh_display": "none",
"browser_client": "recommended",
"warnings": [
{
"code": "h264_floor_not_last"
}
],
"visibility": "user",
"eligibility": "eligible"
}
]
}
]
}

Missing/invalid/expired/revoked token.

Media typeapplication/json
object
error
required
object
code
required

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.

string
message
required
string
live_sessions

Present on restart_required.

integer
session_id

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.

string format: uuid
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.

Array<object>
object
id
required
string format: uuid
name
required
string
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.

Media typeapplication/json
object
error
required
object
code
required

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.

string
message
required
string
live_sessions

Present on restart_required.

integer
session_id

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.

string format: uuid
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.

Array<object>
object
id
required
string format: uuid
name
required
string
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"
}
]
}
}