Skip to content

An app's artwork record + whether a provider is configured.

GET
/v1/admin/apps/{id}/artwork
curl --request GET \
--url http://localhost:8080/v1/admin/apps/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/artwork \
--header 'Authorization: Bearer <token>'
id
required
string format: uuid

OK.

Media typeapplication/json
object
artwork
required
One of:

UI-P7: an app’s artwork provenance and the two locally served crops.

object
app_id
required
string format: uuid
source
required

‘provider’ = matched and fetched automatically. ‘manual’ = an admin picked, supplied or uploaded it. ‘none’ = we looked and there is nothing - a NEGATIVE CACHE and a first-class outcome, not an error: a desktop app is not in a games database and never will be, so recording it stops every sweep re-querying a third party for a row that can never match. All three render correctly; ‘none’ is the gradient tile.

string
Allowed values: provider manual none
provider
required

Which provider produced the match (‘steamgriddb’); empty for a manual or unmatched app.

string
provider_ref
required

The provider’s opaque id for the matched title.

string
matched_name
required

The provider-side title that was matched. Surfaced so an operator can SEE that “Portal” matched “Portal Knights” before deciding to override it.

string
tile_url
required

Local /v1/artwork/… path for the 2:3 portrait tile crop, or null.

string | null
hero_url
required

Local /v1/artwork/… path for the wide hero crop, or null.

string | null
attribution
required

Credit line to render beside the art, when the source asks for one.

string
locked
required

True once an admin has overridden the match. The automatic sweep NEVER touches a locked record - fuzzy matching is wrong sometimes, and a correction must not be silently re-broken.

boolean
updated_at
required
string format: date-time
provider_configured
required

Whether a third-party artwork provider is configured on this deployment. False is the SHIPPED DEFAULT. The admin UI reads this to explain why the provider-backed controls are unavailable, rather than offering a button that silently does nothing. Resolved PER REQUEST from the encrypted secrets store, so a key set from the admin UI flips this without a control-plane restart.

boolean
provider_name
required

Empty when no provider is configured.

string
provider_origin
required

Where the credential in effect came from: ‘database’ (an admin set it through /v1/admin/secrets), ‘environment’ (the legacy QUASAR_STEAMGRIDDB_API_KEY), ‘static’ (a provider supplied directly at construction - embedded/test wiring only, never the shipped server) or ‘none’. An operator upgrading a deployment that already had the env var must be able to SEE that it is still what is being used.

string
Allowed values: none database environment static
provider_problem

Why the provider is unavailable despite something being configured - e.g. a stored API key the master key cannot decrypt. Empty when there is nothing to explain. Never contains any part of a credential.

string
Example
{
"artwork": {
"source": "provider"
},
"provider_origin": "none"
}

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"
}
]
}
}

Authenticated but insufficient role / not the owner (precedes resource lookup).

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"
}
]
}
}