# ScreenRig API reference

> Canonical HTML: https://screenrig.ai/docs/api/
> Last-reviewed: 2026-08-14 · Phase: prerelease localhost v1 · API contract: OpenAPI 0.2.0
> Source SHA-256: f06e62d1e5e3420a23e48b181295b7b6915daf26fbfe5988e8d2af3a960fee0a

ScreenRig is an agent-operated digital-signage service: an agent uses the CLI and REST API to upload content, assemble a looping playlist, pair a browser screen, and observe the result.

Generated from the current OpenAPI contract and problem registry. The public marketing and documentation site is live and verified over HTTPS; the service API, Player, release hosts, protected content delivery, and full production application stack remain undeployed and unverified, and npm packages remain unpublished. Examples target verified localhost service origins.

## Five-minute agent workflow

~~~sh
screenrig account create --email agent-owner@example.com --json
screenrig app upload ./built-webapp --json
screenrig playlist create ./playlist.json --json
screenrig screen pair --code ABCDEF --label "Lobby" --json
screenrig screen assign scr_example --playlist-id pl_example --if-match 1 --json
screenrig events follow --json
~~~

Expected: mutations return revisioned resources or durable operations; event follow emits opaque cursors. Values are placeholders.

## Raw contracts

- [OpenAPI YAML](https://screenrig.ai/docs/api/openapi.yaml)
- [OpenAPI JSON](https://screenrig.ai/docs/api/openapi.json)
- [Problem registry YAML](https://screenrig.ai/docs/api/problems.yaml)
- [Problem registry JSON](https://screenrig.ai/docs/api/problems.json)

## Semantics

https://screenrig.ai/ serves the live, HTTPS-verified marketing and documentation site. Architectural service origins are https://api.screenrig.ai for control and https://play.screenrig.ai for trusted runtime/content; neither service origin, nor release or protected-content production delivery, is deployed or verified. Verified local service equivalents use the same hostnames under localhost:8088. Account bearer, Player cookies, placement capability, release grants, and operator bearer are distinct realms.

Retry ambiguous mutations only with the same idempotency key and identical request. Mutable resources require current If-Match. Treat list and SSE cursors as opaque and resume with the last cursor or Last-Event-ID. On stream.resync_required, refetch authoritative state and resume at the supplied head cursor.

Application upload validates and extracts already-built content and publishes immutable release identity. Media upload follows server-issued signed PUT details verbatim. ScreenRig is not public file hosting; protected release, media, and K/V access requires current manifest authority.

## RFC 9457 problem codes

- internal_error — HTTP 500: Internal server error (https://screenrig.ai/problems/internal-error)
- invalid_request — HTTP 400: Request is invalid (https://screenrig.ai/problems/invalid-request)
- unauthorized — HTTP 401: Authentication is required (https://screenrig.ai/problems/unauthorized)
- forbidden — HTTP 403: Request is not allowed (https://screenrig.ai/problems/forbidden)
- not_found — HTTP 404: Resource was not found (https://screenrig.ai/problems/not-found)
- method_not_allowed — HTTP 405: Method is not allowed (https://screenrig.ai/problems/method-not-allowed)
- idempotency_mismatch — HTTP 409: Idempotency key does not match the original request (https://screenrig.ai/problems/idempotency-mismatch)
- account_exists — HTTP 409: Account already exists (https://screenrig.ai/problems/account-exists)
- resource_conflict — HTTP 409: Resource state conflicts with the request (https://screenrig.ai/problems/resource-conflict)
- revision_conflict — HTTP 412: Resource revision does not match (https://screenrig.ai/problems/revision-conflict)
- invalid_range — HTTP 416: Requested byte range is not satisfiable (https://screenrig.ai/problems/invalid-range)
- quota_exceeded — HTTP 413: Account content quota is exceeded (https://screenrig.ai/problems/quota-exceeded)
- rate_limited — HTTP 429: Request rate is too high (https://screenrig.ai/problems/rate-limited)
- dependency_unavailable — HTTP 503: Required dependency is unavailable (https://screenrig.ai/problems/dependency-unavailable)
- schema_incompatible — HTTP 503: Database schema is incompatible (https://screenrig.ai/problems/schema-incompatible)
- not_ready — HTTP 503: Service is not ready (https://screenrig.ai/problems/not-ready)

## Endpoint inventory

## Service status and capabilities

### GET /.health

Operation: getHealth. Auth: none. Request: none. Responses: 200: HealthResponse (application/json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET 'http://api.screenrig.localhost:8088/.health'
~~~

### GET /.ready

Operation: getReadiness. Auth: none. Request: none. Responses: 200: ReadyResponse (application/json); 503: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET 'http://api.screenrig.localhost:8088/.ready'
~~~

### GET /.version

Operation: getVersion. Auth: none. Request: none. Responses: 200: VersionResponse (application/json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET 'http://api.screenrig.localhost:8088/.version'
~~~

### GET /api/v1/capabilities

Operation: getCapabilities. Auth: none. Request: none. Responses: 200: Capabilities (application/json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET 'http://api.screenrig.localhost:8088/api/v1/capabilities'
~~~

## Accounts and credentials

### POST /api/v1/accounts

Operation: createAccount. Auth: none. Request: AccountCreate. Responses: 201: AccountCreated (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/accounts'
~~~

Schema-generated request body:

~~~json
{
  "email": "string"
}
~~~

### GET /api/v1/account

Operation: getAccount. Auth: accountBearer. Request: none. Responses: 200: Account (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/account'
~~~

### POST /api/v1/account/credential-issuances/{id}/ack

Operation: acknowledgeCredentialIssuance. Auth: accountBearer. Request: none. Responses: 204: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/account/credential-issuances/RESOURCE_ID/ack'
~~~

## Applications and operations

### GET /api/v1/applications

Operation: listApplications. Auth: accountBearer. Request: none. Responses: 200: ApplicationList (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/applications'
~~~

### POST /api/v1/applications

Operation: uploadApplication. Auth: accountBearer. Request: string. Responses: 202: OperationAccepted (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' --header 'ScreenRig-Archive-SHA256: VALUE' --header 'ScreenRig-Expanded-Bytes: VALUE' --header 'ScreenRig-File-Count: VALUE' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/applications'
~~~

Schema-generated request body:

~~~json
"string"
~~~

### GET /api/v1/applications/{id}

Operation: getApplication. Auth: accountBearer. Request: none. Responses: 200: Application (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/applications/RESOURCE_ID'
~~~

### GET /api/v1/operations/{id}

Operation: getOperation. Auth: accountBearer. Request: none. Responses: 200: Operation (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/operations/RESOURCE_ID'
~~~

### POST /api/v1/operations/{id}/cancel

Operation: cancelOperation. Auth: accountBearer. Request: none. Responses: 200: Operation (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/operations/RESOURCE_ID/cancel'
~~~

## Media uploads

### GET /api/v1/media

Operation: listMedia. Auth: accountBearer. Request: none. Responses: 200: MediaList (application/json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/media'
~~~

### POST /api/v1/media/uploads

Operation: createMediaUpload. Auth: accountBearer. Request: MediaUploadDeclaration. Responses: 201: MediaUploadSession (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/media/uploads'
~~~

Schema-generated request body:

~~~json
{
  "filename": "string",
  "content_type": "image/png",
  "bytes": 1,
  "sha256": "string"
}
~~~

### POST /api/v1/media/uploads/{id}/commit

Operation: commitMediaUpload. Auth: accountBearer. Request: MediaCommit. Responses: 202: Operation (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/media/uploads/RESOURCE_ID/commit'
~~~

Schema-generated request body:

~~~json
{
  "content_type": "image/png",
  "bytes": 1,
  "sha256": "string"
}
~~~

### GET /api/v1/media/{id}

Operation: getMedia. Auth: accountBearer. Request: none. Responses: 200: Media (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/media/RESOURCE_ID'
~~~

### DELETE /api/v1/media/{id}

Operation: deleteMedia. Auth: accountBearer. Request: none. Responses: 204: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request DELETE --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/media/RESOURCE_ID'
~~~

## Playlists

### GET /api/v1/playlists

Operation: listPlaylists. Auth: accountBearer. Request: none. Responses: 200: PlaylistList (application/json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/playlists'
~~~

### POST /api/v1/playlists

Operation: createPlaylist. Auth: accountBearer. Request: PlaylistWrite. Responses: 201: Playlist (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/playlists'
~~~

Schema-generated request body:

~~~json
{
  "name": "string",
  "pages": [
    {}
  ]
}
~~~

### GET /api/v1/playlists/{id}

Operation: getPlaylist. Auth: accountBearer. Request: none. Responses: 200: Playlist (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/playlists/RESOURCE_ID'
~~~

### PUT /api/v1/playlists/{id}

Operation: updatePlaylist. Auth: accountBearer. Request: PlaylistWrite. Responses: 200: Playlist (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request PUT --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/playlists/RESOURCE_ID'
~~~

Schema-generated request body:

~~~json
{
  "name": "string",
  "pages": [
    {}
  ]
}
~~~

### DELETE /api/v1/playlists/{id}

Operation: deletePlaylist. Auth: accountBearer. Request: none. Responses: 204: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request DELETE --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/playlists/RESOURCE_ID'
~~~

## Screens and control-plane pairing

### GET /api/v1/screens

Operation: listScreens. Auth: accountBearer. Request: none. Responses: 200: ScreenList (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/screens'
~~~

### POST /api/v1/screens/pair

Operation: pairScreen. Auth: accountBearer. Request: PairScreen. Responses: 201: PairingClaim (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/screens/pair'
~~~

Schema-generated request body:

~~~json
{
  "code": "string",
  "label": "string"
}
~~~

### GET /api/v1/screens/{id}

Operation: getScreen. Auth: accountBearer. Request: none. Responses: 200: Screen (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/screens/RESOURCE_ID'
~~~

### PATCH /api/v1/screens/{id}

Operation: updateScreen. Auth: accountBearer. Request: ScreenPatch. Responses: 200: Screen (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request PATCH --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/screens/RESOURCE_ID'
~~~

Schema-generated request body:

~~~json
{
  "name": "string",
  "playlist_id": "string"
}
~~~

### DELETE /api/v1/screens/{id}

Operation: deleteScreen. Auth: accountBearer. Request: none. Responses: 204: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request DELETE --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/screens/RESOURCE_ID'
~~~

### POST /api/v1/screens/{id}/public-id/rotate

Operation: rotateScreenPublicId. Auth: accountBearer. Request: none. Responses: 200: Screen (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/screens/RESOURCE_ID/public-id/rotate'
~~~

### POST /api/v1/screens/{id}/credential/revoke

Operation: revokeScreenCredential. Auth: accountBearer. Request: none. Responses: 200: Screen (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/screens/RESOURCE_ID/credential/revoke'
~~~

## Application K/V for owners

### GET /api/v1/applications/{application_id}/kv

Operation: listKV. Auth: accountBearer. Request: none. Responses: 200: KVList (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/applications/APPLICATION_ID/kv'
~~~

### GET /api/v1/applications/{application_id}/kv/{key}

Operation: getKV. Auth: accountBearer. Request: none. Responses: 200: KVEntry (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/applications/APPLICATION_ID/kv/KEY'
~~~

### PUT /api/v1/applications/{application_id}/kv/{key}

Operation: putKV. Auth: accountBearer. Request: KVWrite. Responses: 200: KVEntry (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request PUT --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://api.screenrig.localhost:8088/api/v1/applications/APPLICATION_ID/kv/KEY'
~~~

Schema-generated request body:

~~~json
{
  "value_base64": "string",
  "content_type": "string"
}
~~~

### DELETE /api/v1/applications/{application_id}/kv/{key}

Operation: deleteKV. Auth: accountBearer. Request: none. Responses: 204: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request DELETE --header 'Authorization: Bearer SCREENRIG_TOKEN' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://api.screenrig.localhost:8088/api/v1/applications/APPLICATION_ID/kv/KEY'
~~~

## Durable account events

### GET /api/v1/events

Operation: listEvents. Auth: accountBearer. Request: none. Responses: 200: EventList (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/events'
~~~

### GET /api/v1/events/stream

Operation: streamEvents. Auth: accountBearer. Request: none. Responses: 200: string (text/event-stream); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'Authorization: Bearer SCREENRIG_TOKEN' 'http://api.screenrig.localhost:8088/api/v1/events/stream'
~~~

## Player pairing and sessions

### POST /runtime/v1/pairing-sessions

Operation: startPairingSession. Auth: none. Request: none. Responses: 201: PairingSession (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST 'http://play.screenrig.localhost:8088/runtime/v1/pairing-sessions'
~~~

### GET /runtime/v1/pairing-events

Operation: streamPairingEvents. Auth: pairingCookie. Request: none. Responses: 200: string (text/event-stream); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/pairing-events'
~~~

### POST /runtime/v1/pairing-sessions/complete

Operation: completePairingSession. Auth: pairingCookie. Request: PairingComplete. Responses: 200: PairingCompletion (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --cookie 'SCREENRIG_COOKIE=SESSION' --header 'Content-Type: application/json' --data '@request.json' 'http://play.screenrig.localhost:8088/runtime/v1/pairing-sessions/complete'
~~~

Schema-generated request body:

~~~json
{
  "completion_nonce": "string"
}
~~~

### POST /runtime/v1/sessions

Operation: createAnonymousRuntimeSession. Auth: none. Request: RuntimeSessionRequest. Responses: 201: RuntimeSession (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --header 'Content-Type: application/json' --data '@request.json' 'http://play.screenrig.localhost:8088/runtime/v1/sessions'
~~~

Schema-generated request body:

~~~json
{
  "public_id": "string"
}
~~~

### POST /runtime/v1/device-sessions

Operation: createDeviceRuntimeSession. Auth: pairedDeviceCookie. Request: none. Responses: 201: RuntimeSession (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/device-sessions'
~~~

## Runtime manifest, reports, and events

### GET /runtime/v1/manifest

Operation: getRuntimeManifest. Auth: runtimeCookie. Request: none. Responses: 200: RuntimeManifest (application/json); 304: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/manifest'
~~~

### GET /runtime/v1/events

Operation: streamRuntimeEvents. Auth: runtimeCookie. Request: none. Responses: 200: string (text/event-stream); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/events'
~~~

### POST /runtime/v1/reports

Operation: createRuntimeReport. Auth: pairedRuntimeCookie. Request: RuntimeReport. Responses: 202: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --cookie 'SCREENRIG_COOKIE=SESSION' --header 'Content-Type: application/json' --data '@request.json' 'http://play.screenrig.localhost:8088/runtime/v1/reports'
~~~

Schema-generated request body:

~~~json
{
  "severity": "info",
  "code": "manifest.activated",
  "context": null
}
~~~

### POST /runtime/v1/manifests/{manifest_revision}/releases/{release_id}/launch

Operation: launchApplicationRelease. Auth: runtimeCookie. Request: none. Responses: 201: ReleaseLaunch (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request POST --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/manifests/MANIFEST_REVISION/releases/RELEASE_ID/launch'
~~~

## Runtime application K/V

### GET /runtime/v1/apps/{application_id}/kv

Operation: listRuntimeKV. Auth: runtimeCookie + placementCapability. Request: none. Responses: 200: KVList (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'ScreenRig-Placement-Capability: PLACEMENT_CAPABILITY' --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/apps/APPLICATION_ID/kv'
~~~

### GET /runtime/v1/apps/{application_id}/kv/{key}

Operation: getRuntimeKV. Auth: runtimeCookie + placementCapability. Request: none. Responses: 200: KVEntry (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --header 'ScreenRig-Placement-Capability: PLACEMENT_CAPABILITY' --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/runtime/v1/apps/APPLICATION_ID/kv/KEY'
~~~

### PUT /runtime/v1/apps/{application_id}/kv/{key}

Operation: putRuntimeKV. Auth: pairedRuntimeCookie + placementCapability. Request: KVWrite. Responses: 200: KVEntry (application/json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request PUT --header 'ScreenRig-Placement-Capability: PLACEMENT_CAPABILITY' --cookie 'SCREENRIG_COOKIE=SESSION' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'http://play.screenrig.localhost:8088/runtime/v1/apps/APPLICATION_ID/kv/KEY'
~~~

Schema-generated request body:

~~~json
{
  "value_base64": "string",
  "content_type": "string"
}
~~~

### DELETE /runtime/v1/apps/{application_id}/kv/{key}

Operation: deleteRuntimeKV. Auth: pairedRuntimeCookie + placementCapability. Request: none. Responses: 204: no body; default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request DELETE --header 'ScreenRig-Placement-Capability: PLACEMENT_CAPABILITY' --cookie 'SCREENRIG_COOKIE=SESSION' --header 'If-Match: "1"' --header 'Idempotency-Key: REQUEST_ID' 'http://play.screenrig.localhost:8088/runtime/v1/apps/APPLICATION_ID/kv/KEY'
~~~

## Protected content and release assets

### GET /content/v1/manifests/{manifest_revision}/media/{media_id}

Operation: getProtectedMedia. Auth: runtimeCookie. Request: none. Responses: 200: no body; 206: no body; 416: Problem (application/problem+json); 429: Problem (application/problem+json); default: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --cookie 'SCREENRIG_COOKIE=SESSION' 'http://play.screenrig.localhost:8088/content/v1/manifests/MANIFEST_REVISION/media/MEDIA_ID'
~~~

### GET /{asset_path}

Operation: consumeLaunchTicketOrGetReleaseAsset. Auth: releaseLaunchTicket OR releaseGrantCookie. Request: none. Responses: 200: no body; 302: no body; 403: Problem (application/problem+json); 404: Problem (application/problem+json).

Contract-generated localhost request shape:

~~~sh
curl --noproxy '*' --request GET --cookie 'SCREENRIG_COOKIE=SESSION' 'https://r-RELEASE_HOST.apps.screenrig.ai/index.html'
~~~

## Sitemap

See the [public documentation sitemap](https://screenrig.ai/sitemap.md).
