# Screens API reference

> Canonical HTML: https://screenrig.ai/docs/api/screens
> Source SHA-256: e0c997a2ab813502f0d99a41d2ed1d3d6b26dbaf1a3d7e514c1cce0278ff8422

Screens. This group has 84 operations, each with its authentication, parameters, request body fields, request template and responses. The [API overview](https://screenrig.ai/docs/api.md) covers services, authentication, requests and retries, problem codes and versioning.

Replace uppercase placeholders. Save a schema-valid JSON body in request.json when required. Cookie examples use a local cookie jar from an authenticated session.

## Screens

### GET /api/screens

**List Screens**

Default list returns pairing_pending and active screens. Pass state=archived to list archived screens only. Pass tag to list only screens whose tags contain that exact tag; untagged screens are omitted when the filter is present.

Pass field, once per field, as NAME:VALUE to list only screens whose fields hold every value, and near as LAT,LON,KM to list only screens located within KM kilometres of the point; screens with no location are omitted, and every filter combines with state and tag.

Pass folder as a screen folder ID to list only the screens directly inside it (not those of its subfolders), or root for the screens outside every folder.

Screens are newest first, at most 500 per page. next_cursor is the position of the last returned row while more rows remain; pass it back as after, with the same filters, for the next page. next_cursor is null on the last page. A caller limited to screens (docs/screen-scope.md) lists only the screens in its scope.

A request that names no project leaves out each project where the caller is limited to screens; name the project to list it.


Operation: listScreens.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| state | query | optional | Omit for pairing_pending and active. Pass archived to list archived screens only. |
| tag | query | optional | Exact screen tag. Untagged screens are omitted when this filter is present. |
| field | query | optional | NAME:VALUE, split at the first colon: only screens whose field NAME holds exactly VALUE. Repeat the parameter for several fields; every one must match, and a name may be given once. A malformed or repeated one is invalid_request naming field. |
| near | query | optional | LAT,LON,KM in decimal degrees and kilometres, for example 47.6062,-122.3321,25: only screens whose location lies within KM kilometres of the point. Latitude is -90 to 90, longitude -180 to 180 and KM above 0 and at most 20038. Screens with no location are omitted. A malformed value is invalid_request naming near. |
| folder | query | optional | A screen folder ID keeps only the screens directly inside that folder, not those of its subfolders; root keeps only the screens outside every folder. Omitted lists every screen. |
| after | query | optional | next_cursor of the previous page of the same list, with the same filters. Omitted starts at the newest row. A value that is not such a cursor is invalid_request. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screens | ScreenList (application/json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screen-folders

**List Screen Folders**

Lists every screen folder in the project, at most 1000, ordered by name.

Folders organize screens and target them: a campaign audience leaf {"folder": ID} matches the screens in that folder and every folder inside it, a fleet action selector by:folder acts on the same screens, and a folder's default_playlist_id is what its screens without a playlist of their own show. parent_id is null at the project root; build the tree from parent_id. screens counts the live screens directly inside each folder.

A screen's folder is Screen.folder_id. A caller limited to screens (docs/screen-scope.md) lists the folders inside its scope and the folders above them, which are read-only so its tree has a root; folders above carry no screens count and sibling folders are hidden. A request that names no project leaves out each project where the caller is limited to screens.


Operation: listScreenFolders.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screen-folders'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen folders | ScreenFolderList (application/json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screen-folders

**Create Screen Folder**

Creates one screen folder at the project root (parent_id null or absent) or inside parent_id. Names are trimmed, hold 1 to 100 Unicode characters with no controls, are not "." or "..", and are unique among their siblings (resource_conflict).

An organization holds at most 1000 folders across its projects (quota_exceeded) nested at most 10 deep (invalid_request). default_playlist_id, when set, must name an approved playlist (playlist_not_approved). Emits screen.folder_created.

A caller limited to screens (docs/screen-scope.md) creates a folder only inside a folder of its scope; the project root or any other parent is outside_screen_scope. Creating a folder inside a protected folder is a protected_change review (202 pending_review) decided by a second approver.


Operation: createScreenFolder.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenFolderCreate

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| name | required | string | Trimmed; 1 to 100 Unicode characters, no controls, not . or .., unique among its siblings. minLength: 1 maxLength: 100 |
| parent_id | optional | inline | The folder to create it in; null or absent for the project root. |
| default_playlist_id | optional | string | An approved playlist for the folder's screens without their own. minLength: 1 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screen-folders'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | Created folder. | ScreenFolder (application/json); headers: ETag |
| 202 | The write became a review and waits for a person's approval (docs/reviews.md), under Propose access, or at any access level when it reaches a screen in a protected screen folder, where a second approver decides it (docs/screen-scope.md). A person approves, rejects or requests changes in the dashboard at review.url. An Idempotency-Key replay answers the same body. | ReviewPending (application/json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screen-folders/{id}

**Get Screen Folder**

A caller limited to screens (docs/screen-scope.md) reads the folders of its scope and the folders above them; any other folder, and an unknown id, is outside_screen_scope.


Operation: getScreenFolder.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screen-folders/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | One screen folder. | ScreenFolder (application/json); headers: ETag |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PATCH /api/screen-folders/{id}

**Update Screen Folder**

Renames a folder (name), moves it with everything inside (parent_id, null for the project root) or sets the default playlist its screens without their own show (default_playlist_id, null clears it). A folder cannot move into itself or a folder inside it, and the move must keep every folder within 10 levels (invalid_request).

A move or a default change re-resolves the screens inside within about 15 seconds, and the response's levels name the campaigns and folder defaults those screens gain or lose. A change advances the folder revision and emits screen.folder_updated; the same values are a no-op.

If-Match optionally guards the revision. protected true protects the folder and every folder inside it, and false clears it; send it alone (invalid_request otherwise). Only a person who can approve sets it: an agent, a service client or a member who cannot approve gets approver_required.

A change appends screen.folder_protection_changed (details folder_id, protected). Clearing protection, and a move or a default playlist change on a folder that holds protected screens, is a protected_change review (202 pending_review) decided by a second approver.

A caller limited to screens (docs/screen-scope.md) changes only folders inside its scope: parent_id must name one too (the root is outside_screen_scope), and protected is not its to change on a folder its scope names. protected true is protected folders, which the Enterprise plan includes; a project whose plan lacks it is refused with feature_not_in_plan (402).

Protection that is set keeps holding changes for a second approver, and asking to remove it is a review on every plan.


Operation: updateScreenFolder.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenFolderPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| name | optional | string | Trimmed; 1 to 100 Unicode characters, no controls, not . or .., unique among its siblings. minLength: 1 maxLength: 100 |
| parent_id | optional | inline | Move the folder into this one; null for the project root. |
| default_playlist_id | optional | inline | An approved playlist for the folder's screens without their own; null clears it. |
| protected | optional | boolean | Protects the folder and every folder inside it (docs/screen-scope.md). Only a person who can approve sets it. Send it alone: with any other member the request is invalid_request. Clearing it on a protected folder is itself a protected change, a review decided by a second approver (202 pending_review). |

#### Request template

~~~sh
curl --request PATCH --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screen-folders/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Updated folder. | ScreenFolder (application/json); headers: ETag |
| 202 | The write became a review and waits for a person's approval (docs/reviews.md), under Propose access, or at any access level when it reaches a screen in a protected screen folder, where a second approver decides it (docs/screen-scope.md). A person approves, rejects or requests changes in the dashboard at review.url. An Idempotency-Key replay answers the same body. | ReviewPending (application/json) |
| 402 | feature_not_in_plan — Protected folders are part of the Enterprise plan; the problem names the feature, the plan the account is on and required_plan. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/screen-folders/{id}

**Delete Screen Folder**

Deletes one folder. Nothing inside is deleted: its screens and folders move up into the deleted folder's parent (the project root when it had none), each moved folder advancing its revision, and those screens re-resolve.

Refused with resource_conflict while a campaign audience names the folder, so no schedule loses its screens silently: change or delete those campaigns first. Answers the levels its screens gained or lost. Emits screen.folder_deleted. If-Match optionally guards the revision.

A caller limited to screens (docs/screen-scope.md) deletes only a folder inside its scope that its scope does not name itself (outside_screen_scope); the screens inside move up into the parent. Deleting a protected folder, or one that holds protected screens, is a protected_change review (202 pending_review) decided by a second approver.


Operation: deleteScreenFolder.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screen-folders/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Deleted; its contents now sit in its parent. levels names the folder default playlists its screens gained or lost. | ScreenFolderDeleteResult (application/json) |
| 202 | The write became a review and waits for a person's approval (docs/reviews.md), under Propose access, or at any access level when it reaches a screen in a protected screen folder, where a second approver decides it (docs/screen-scope.md). A person approves, rejects or requests changes in the dashboard at review.url. An Idempotency-Key replay answers the same body. | ReviewPending (application/json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/move

**Move Screens**

Moves screens (screen_ids) and folders (folder_ids) into one folder (folder_id), or to the project root when folder_id is null, in one all-or-nothing request of at most 1000 ids. Every id must exist (not_found). A folder cannot move into itself or a folder inside it, and the move must keep every folder within 10 levels (invalid_request).

Items already in the destination are left as they are; each moved screen and folder advances its revision. Moved screens re-resolve their playlist within about 15 seconds: levels names the campaigns whose folder audience they enter or leave and the folder default playlists they gain or lose.

Emits screen.moved, whose details carry the counts (screens, folders) and where each moved item was: moves [{screen_id, from}] (from is the folder id, empty for the root) and folder_moves [{folder_id, from_parent}], at most 1000 each, so a history can move them back with this route.

A caller limited to screens (docs/screen-scope.md) moves screens and folders inside its scope into a folder of its scope; any id or destination outside it, and the project root, refuse the whole move (outside_screen_scope), also when a screen leaves the scope between the check and the write.

A move into or out of a protected folder is a protected_change review (202 pending_review) decided by a second approver.


Operation: moveScreens.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenMove

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| folder_id | required | inline | Destination folder; null for the project root. |
| screen_ids | optional | array |  |
| folder_ids | optional | array |  |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/move'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | How many screens and folders moved, and the levels the moved screens gain or lose. | ScreenMoveResult (application/json) |
| 202 | The write became a review and waits for a person's approval (docs/reviews.md), under Propose access, or at any access level when it reaches a screen in a protected screen folder, where a second approver decides it (docs/screen-scope.md). A person approves, rejects or requests changes in the dashboard at review.url. An Idempotency-Key replay answers the same body. | ReviewPending (application/json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/schedule/calendar

**Get Schedule Calendar**

Answers what a subject's screens play over [from, to) and why, for the dashboard's Schedule calendar: exactly one of screen_id, folder_id (the screens in the folder and every folder inside it), tag, or campaign_id (the screens in its audience).

Screens whose stacks have identical inputs (the same levels, default, timezone and display count) share one group with one timeline, so a large subject costs tens of timelines; a group lists up to 500 of its screens and an answer at most 200 groups (truncated).

Each group carries its stack, highest first (takeover, campaigns that reach the screens, their own schedule entries, then the default playlist or the nearest folder's), each level's spans (plays on every display it targets, partial with the displays it plays on, outranked, or conditional while it waits on interaction or a data condition only a Player observes), what each display shows (on_screen; the main region of a full page), and conflicts (two campaigns of one priority holding at once; a level that holds yet never plays). from and to are RFC 3339 instants, to after from, at most 93 days apart; they are truncated to the minute.

Rules are read in each group's timezone as the sweep and every Player read them; every level is judged playable. A caller limited to screens (docs/screen-scope.md) must send a screen_id or a folder_id inside its scope; a tag, a campaign_id or no subject is outside_screen_scope.


Operation: getScheduleCalendar.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| from | query | required |  |
| to | query | required |  |
| screen_id | query | optional |  |
| folder_id | query | optional |  |
| tag | query | optional |  |
| campaign_id | query | optional |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/schedule/calendar?from=FROM&to=TO'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The subject's calendar. | ScheduleCalendar (application/json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/pair

**Pair Screen**

Claims one fleet-global six-character Player pairing session. Exact Idempotency-Key retries return the durable original result without consuming quota again.

The dashboard session claims into its current project with the same body, Idempotency-Key, budgets and problems, except that its not_found for an unknown or already-claimed code names no CLI command; it is a state change, so it follows the dashboard mutation rules, a shadow session without staff write mode is refused, and the events the claim appends name the dashboard user. folder_id names the folder the screen lands in.

A caller limited to screens (docs/screen-scope.md) must send a folder inside its scope and pairs only as a person who can approve (outside_screen_scope, approver_required).


Operation: pairScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: PairScreen

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Idempotency-Key | header | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| code | required | string | pattern: "^[23456789ABCDEFGHJKMNPQRSTUVWXYZ]{6}$" |
| label | optional | string | minLength: 1 maxLength: 120 |
| folder_id | optional | string | The screen folder the screen lands in; omitted lands it at the project root. A caller limited to screens must send a folder inside its scope (outside_screen_scope otherwise). A folder the project does not hold is invalid_request for a caller of the whole project. Part of the idempotent request: another folder_id under the same Idempotency-Key is idempotency_mismatch. minLength: 1 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/pair'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | Durable pairing_pending screen claim. | PairingClaim (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 413 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 500 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/provision

**Provision Screen**

Optional browser-only new-screen provisioning. Always creates one new pairing_pending screen; at most five wait to pair at once (quota_exceeded). Exact retries redeliver the same fragment token for ten minutes. Six-character pairing remains the default and only recovery flow. folder_id names the folder the screen lands in.

A caller limited to screens (docs/screen-scope.md) must send a folder inside its scope (outside_screen_scope otherwise).


Operation: provisionScreen.

- Authentication: projectBearer
- Request body: ProvisionScreen

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Idempotency-Key | header | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| label | optional | string | minLength: 1 maxLength: 120 |
| folder_id | optional | string | The screen folder the screen lands in, as on PairScreen. minLength: 1 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/provision'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | New browser provisioning link. | ScreenProvisioning (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 410 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 413 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 500 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/actions

**Apply Screen Actions**

Applies one action to a selected fleet with no screen-count cap. Select unique screen_ids or a tag matching active screens in creation order. The resolved selection is pinned before any mutation, stored in bounded chunks, and kept for twenty-four hours under Idempotency-Key.

Each step uses its single-screen mutation path, preserving revisions, events, runtime signals and per-screen limits. A response contains one bounded page of results; matched, succeeded and failed describe that page. HTTP 202 includes total, offset and next_cursor. Continue with the same selector, action and Idempotency-Key, passing next_cursor as after.

Honor Retry-After when present, including a page with zero results. The response echoes Idempotency-Key, including a generated key when the caller omitted it; retain this key for continuations. HTTP 200 finishes the selection. Answered pages replay exactly, including per-screen failures; interrupted steps replay without repeating their effects.

Large actions are paced against the existing 600-per-minute organization budgets for reload, toast, takeover, playlist schedules, reboot, display and display schedules; organization exhaustion pauses the action instead of refusing the fleet. Per-screen exhaustion fails that screen alone.

An assign, takeover or set_playlist_schedule step that binds a playlist with a format to a screen whose wall has another shape fails that screen alone with 409 playlist_format_mismatch. Assign and tag changes have no message budget.

An assign whose playlist has an iframe src or application params that read a screen token some screen of the page lacks adds a screen_fact_missing warning to that page (warnings, PlaylistWarning). Partial failure is a normal result. All pages and retries under one key are one metered action.

Requests use a bounded byte payload; use a tag for a selection larger than the transport payload. A malformed selector or action is refused before any screen is touched.

A caller limited to screens (docs/screen-scope.md) selects by screen_ids or by folder, every screen inside its scope; a tag selector, a screen outside the scope or an unknown one refuses the whole action (outside_screen_scope) before anything is pinned or proposed.

Assign, takeover, takeover_clear, set_playlist_schedule and clear_playlist_schedule that reach a protected screen become a review decided by a second approver (202 pending_review) at every access level; set_tags, add_tags, remove_tags, toast, display, display_clear, set_display_schedule and clear_display_schedule on a protected screen are protected_target (409).

A set_playlist_schedule action with a paused entry (a draft) is schedule change control, which the Enterprise plan includes; a project whose plan lacks it is refused with feature_not_in_plan (402).


Operation: applyScreenActions.

- Authentication: projectBearer
- Request body: ScreenActionRequest

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |
| after | query | optional | next_cursor from the preceding result page. Reuse the original Idempotency-Key and request body. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| selector | required | ScreenActionSelector |  |
| action | required | ScreenAction | One fleet action, discriminated by type. Each member carries only its own parameters; a member of another type is an unknown field and invalid_request. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/actions'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Per-screen results. Partial success is a normal answer. | ScreenActionResult (application/json); headers: Idempotency-Key, Cache-Control, RateLimit-Policy, RateLimit |
| 202 | More screens remain (continue with next_cursor and the same Idempotency-Key and body, honoring Retry-After), or, with status pending_review, the action became a review under Propose access (docs/reviews.md), or at any access level because it reaches a screen in a protected screen folder (docs/screen-scope.md). Only assign, takeover, takeover_clear, set_playlist_schedule and clear_playlist_schedule become reviews; every other action is insufficient_access for Propose. | inline (application/json); headers: Idempotency-Key, Retry-After, Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 402 | feature_not_in_plan — set_reboot_schedule with enabled true (Scheduled reboots) and set_playlist_schedule with a displays lock (Video walls) are part of Plus; the clearing actions and one-off commands are open. payment_required — the project has no credit left. The problem names the feature, the project's plan and required_plan. | Problem (application/problem+json) |
| 403 | insufficient_access, outside_screen_scope when a caller limited to screens selects outside its scope, or approver_required for a member limited to screens who cannot approve. | Problem (application/problem+json) |
| 409 | playlist_format_mismatch and the other conflicts of the action, or protected_target when set_tags, add_tags, remove_tags, toast, display, display_clear, set_display_schedule or clear_display_schedule reaches a screen in a protected screen folder. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Get Screen**

Returns pairing_pending, active, and archived screens. Deleted screens are not found.


Operation: getScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen | Screen (application/json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Update Screen**

Updates the screen's name, playlist_id, timezone, wall, tags, fields, location, log_level, toast_level, display or kiosk. A tag change answers levels: the campaigns the screen enters or leaves, with each campaign's reach before and after. wall sets the screen's video wall (an object) or clears it (null).

Setting one needs a Player that declared the display.wall host capability, else 409 wall_unsupported.

Changing it remints the manifest revision and appends screen.updated with details.wall (the wall object, or null once cleared). fields merges into the screen's own values (a string sets a field, null removes it, a null fields clears every field) and location sets where the screen is (null clears it).

Changing either remints the manifest revision of a screen that shows a playlist, so its card placements expand their {screen.…} tokens again, and appends screen.updated with details.fields (the complete new object, {} once cleared) or details.location (the new object, or null once cleared).

A name or value the grammar refuses, or a thirty-third field, is invalid_request naming it. log_level (debug, info, warn, error) and toast_level (info, alert, error) set the lowest level the Player generates or shows, and null clears each; any other value is invalid_request.

Changing either remints the manifest revision, which carries them as the root member verbosity. display sets or clears the display's idle-dark and brightness settings: display.idle {after_s, wake} darkens the glass after after_s seconds (30 to 86400) without a touch or key or, when wake lists presence, anybody in front of the screen, and a wake event (touch, presence, or both, each once) lights it at once; display.brightness {mode} is auto (the device's light sensor sets the brightness) or fixed (the default, the operator's brightness).

A member you send replaces it, null clears it, a member you leave out stays, and a null display clears both. kiosk {lock} true pins the Player to the screen (Android screen pinning) and false or null releases it. The display schedule and override keep their own routes; a display member naming schedule or override is invalid_request.

A value out of range or of the wrong type is invalid_request naming the path (display.idle.after_s).

Changing any of them remints the manifest revision of a screen that shows a playlist and changes the manifest ETag of every screen; the manifest carries display.idle and display.brightness in the root display member and the lock as the root member kiosk, and screen.manifest_changed is appended; the screen reads them back as Screen.display.idle, Screen.display.brightness and Screen.kiosk.

The backend stores what you send whatever the Player: a Player applies a setting only when its host capabilities declare display_power.idle, brightness.auto or kiosk_lock, and older Players ignore them. Assigning a playlist whose format does not match the screen's wall is 409 playlist_format_mismatch.

Under Propose access a patch of playlist_id alone becomes a review (202 pending_review); any other field, wall, fields, location, display and kiosk included, is insufficient_access. A playlist with no approved version is playlist_not_approved.

When the playlist the screen then shows has an iframe src or application params that read a screen token the screen lacks, the answer carries a screen_fact_missing warning (warnings, PlaylistWarning) and the screen's manifest leaves that primitive out.

On a protected screen playlist_id is a review decided by a second approver (202 pending_review) at every access level and is sent alone (invalid_request with name); tags, fields, location, timezone, wall and display are protected_target (409), also beside playlist_id. Rename stays live. Clearing playlist_id (null) on an archived screen is 409 screen_archived.


Operation: updateScreen.

- Authentication: projectBearer
- Request body: ScreenPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| name | optional | string | minLength: 1 maxLength: 120 |
| playlist_id | optional | inline | The screen default: a playlist id assigns it; null clears it; omission preserves it. Takeovers, campaigns, schedules and inherited folder defaults retain precedence. Without effective content the paired Player shows its idle display. |
| timezone | optional | string | IANA time zone identifier, for example America/Los_Angeles. It is the zone every page visibility rule on this screen is evaluated in. Changing it remints the manifest revision. A screen assigned a playlist that uses page visibility must have one. minLength: 1 maxLength: 64 |
| wall | optional | inline | The screen's video wall; null clears it. Setting one needs a Player that declared the display.wall host capability, else 409 wall_unsupported. Changing it remints the manifest revision. |
| tags | optional | ScreenTags | Fleet selector tags, 0 to 16 unique exact tags, each matching the media tag grammar. On ScreenPatch the array replaces the whole set and an empty array clears it; a duplicate or malformed tag is invalid_request. Changing tags bumps the screen revision (If-Match guards it) and appends screen.updated with details.tags; it never changes the manifest revision. Tags are never on the runtime manifest and never authorization. |
| fields | optional | inline | Merges into the screen's own values, such as a store number or a region: a string sets a field, null removes it, and a null fields clears every field. Names are 1 to 32 lower case letters, digits or underscores; values 1 to 200 characters after trimming; a screen carries at most 32 fields. Changing the fields remints the manifest revision of a screen that shows a playlist. Card placements read them through {screen.field.NAME} tokens. |
| location | optional | inline | Where the screen is; null clears it. Changing it remints the manifest revision of a screen that shows a playlist. Card placements read it through the {screen.location} token. |
| log_level | optional | inline | The lowest log level the screen's Player generates: it writes no log line below it, and error lines are always generated; null clears it (the Player's default). Changing it remints the manifest revision, which carries it as verbosity.logLevel. |
| toast_level | optional | inline | The lowest toast level the screen's Player shows: it shows no toast below it, and error toasts are always shown; null clears it (every toast shows). Changing it remints the manifest revision, which carries it as verbosity.toastLevel. |
| display | optional | inline | The display's idle-dark and brightness settings (display.idle, display.brightness). A member you send replaces it, null clears it, a member you leave out stays; a null display clears both. Changing either remints the manifest revision of a screen that shows a playlist, and the manifest carries them in the root display member. On a protected screen it is protected_target. |
| kiosk | optional | inline | The kiosk lock (kiosk.lock). lock true pins the Player to the screen; false or null releases it. Changing it remints the manifest revision of a screen that shows a playlist, and the manifest carries it as the root member kiosk. |

#### Request template

~~~sh
curl --request PATCH --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The write became a review and waits for a person's approval (docs/reviews.md), under Propose access, or at any access level when it reaches a screen in a protected screen folder, where a second approver decides it (docs/screen-scope.md). A person approves, rejects or requests changes in the dashboard at review.url. An Idempotency-Key replay answers the same body. | ReviewPending (application/json) |
| 200 | Screen | Screen (application/json) |
| 402 | feature_not_in_plan — Video walls are part of Plus: setting a wall other than the screen's own needs it, null clears it in every plan. The problem names the feature, the project's plan and required_plan. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| 409 | protected_target - the change reaches a screen in a protected screen folder and has no review form (docs/screen-scope.md). Clear the folder's protection through a review, make the change, and protect the folder again. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Delete Screen**

Soft-deletes an eligible screen and immediately revokes its cookie credentials and runtime grants. Ordinary reads then return not_found; the tombstoned row is retained for at least 90 days before cleanup. If-Match guards the revision and Idempotency-Key replays the result.

A native identity-bound screen returns screen_archive_required: use archive to retain that binding; an identity-bound screen that is already archived stays archived (the same problem, saying so). This route does not grant administrative break-glass permission to release native enrollment. A screen in a protected screen folder is protected_target (409).


Operation: deleteScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Screen soft-deleted; its tombstone is retained for at least 90 days. | no body |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | screen_archive_required for a native identity-bound screen, or protected_target when the screen is in a protected screen folder (docs/screen-scope.md). | Problem (application/problem+json) |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Patch Screen Tags**

A signed-in person sets a screen's tags. tags is the complete new set: an empty array clears it, and a malformed or repeated tag is invalid_request at tags[i].

It is the tag change updateScreen makes, on the dashboard session: the screen revision moves by one, screen.updated carries details.tags and the person as its actor, and the manifest revision never changes. A pairing_pending, active or archived screen accepts it.

The body has no other member: a session renames a screen with patchScreenName and assigns its playlist with patchScreenPlaylist, and cannot change its timezone. If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale).

The dashboard session only; agents change tags with updateScreen or a set_tags, add_tags or remove_tags fleet action. listScreens with tag finds the screen by its new tags. On a protected screen the change is protected_target (409).


Operation: patchScreenTags.

- Authentication: dashboardSession
- Request body: ScreenTagsPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| tags | required | ScreenTags | Fleet selector tags, 0 to 16 unique exact tags, each matching the media tag grammar. On ScreenPatch the array replaces the whole set and an empty array clears it; a duplicate or malformed tag is invalid_request. Changing tags bumps the screen revision (If-Match guards it) and appends screen.updated with details.tags; it never changes the manifest revision. Tags are never on the runtime manifest and never authorization. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/tags'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Patch Screen Fields**

A signed-in person changes a screen's fields with updateScreen's merge patch: a string sets a field, null removes it, and a null fields clears every field. A refused name or value, or a thirty-third field, is invalid_request at fields or fields.NAME.

The screen revision moves by one, a screen that shows a playlist gets a new manifest revision, and the event carries details.fields and the person as its actor. The body has no other member. If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale).

The dashboard session only; agents use PATCH /api/screens/{id} (updateScreen). On a protected screen the change is protected_target (409).


Operation: patchScreenFields.

- Authentication: dashboardSession
- Request body: ScreenFieldsPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| fields | required | inline | Merges into the screen's own values, such as a store number or a region: a string sets a field, null removes it, and a null fields clears every field. Names are 1 to 32 lower case letters, digits or underscores; values 1 to 200 characters after trimming; a screen carries at most 32 fields. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/fields'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Patch Screen Location**

A signed-in person sets or clears where a screen is with updateScreen's location: an object with latitude, longitude and an optional name and address replaces the location whole, and null clears it. A missing or mistyped member, a coordinate out of range, a name over 40 characters or an address over 200 is invalid_request at location.MEMBER.

The screen revision moves by one, a screen that shows a playlist gets a new manifest revision, and the event carries details.location and the person as its actor. The body has no other member. If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale).

The dashboard session only; agents use PATCH /api/screens/{id} (updateScreen). On a protected screen the change is protected_target (409).


Operation: patchScreenLocation.

- Authentication: dashboardSession
- Request body: ScreenLocationPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| location | required | inline | Where the screen is: the object replaces the whole location and null clears it. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/location'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Patch Screen Verbosity**

A signed-in person sets or clears a screen's log level and toast level with updateScreen's log_level (debug, info, warn, error) and toast_level (info, alert, error): a string sets the level, null clears it, and a member left out stays as it is. Any other value is invalid_request at log_level or toast_level.

The screen revision moves by one, a screen that shows a playlist gets a new manifest revision, and the Player reads the levels from the manifest member verbosity. The body names at least one of the two members and no other. If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale).

The dashboard session only; agents use PATCH /api/screens/{id} (updateScreen).


Operation: patchScreenVerbosity.

- Authentication: dashboardSession
- Request body: ScreenVerbosityPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| log_level | optional | inline | The lowest log level the screen's Player generates; error lines are always generated. null clears it (the Player's default). Changing it remints the manifest revision. |
| toast_level | optional | inline | The lowest toast level the screen's Player shows; error toasts are always shown. null clears it (every toast shows). Changing it remints the manifest revision. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/verbosity'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PATCH /api/screens/{id}/device-settings

**Patch Screen Device Settings**

A signed-in person sets or clears a screen's idle-dark and brightness settings and its kiosk lock with updateScreen's display and kiosk members: display.idle {after_s 30 to 86400, wake touch, presence or both}, display.brightness {mode auto or fixed} and kiosk.lock.

A member you send replaces it, null clears it, and a member left out stays as it is; a null display clears both. A value out of range or of the wrong type is invalid_request naming the path (display.idle.after_s).

The screen revision moves by one, a screen that shows a playlist gets a new manifest revision, and the Player reads the settings from the manifest members display (idle, brightness) and kiosk. The body names at least one of display and kiosk and no other member.

If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale). On a protected screen a display change is protected_target (409). The dashboard session only; agents use PATCH /api/screens/{id} (updateScreen).


Operation: patchScreenDeviceSettings.

- Authentication: dashboardSession
- Request body: ScreenDeviceSettingsPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| display | optional | inline | display.idle and display.brightness, as updateScreen's display: a member you send replaces it, null clears it, a member you leave out stays; a null display clears both. Changing either remints the manifest revision of a screen that shows a playlist. On a protected screen it is protected_target. |
| kiosk | optional | inline | kiosk.lock, as updateScreen's kiosk: lock true pins the Player to the screen, false or null releases it. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/device-settings'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Patch Screen Name**

A signed-in person renames a screen with updateScreen's name. The name is trimmed and must be 1 to 120 characters without control characters, else invalid_request at name (so is a missing or null name).

The screen revision moves by one and the event is screen.manifest_changed, addressed to the screen, with the person as its actor; the manifest revision stays, so playback is untouched and Players refresh the name from the changed ETag. A rename to the same name still moves the revision and appends screen.updated. The body has no other member.

If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale). The dashboard session only; agents use PATCH /api/screens/{id} (updateScreen).


Operation: patchScreenName.

- Authentication: dashboardSession
- Request body: ScreenNamePatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| name | required | string | The screen's name, trimmed; 1 to 120 characters after trimming, without control characters. minLength: 1 maxLength: 120 |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/name'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Patch Screen Playlist**

A signed-in person assigns the screen's playlist with updateScreen's assignment: the playlist must belong to the project and have an approved version (playlist_not_approved), and a playlist with a format needs a screen whose wall has the same shape (409 playlist_format_mismatch).

The screen revision moves by one and the manifest is reissued from the new playlist; while a takeover, a campaign or a schedule entry is effective the playlist becomes the default behind it. Events carry the person as their actor.

The write is live, so only the project owner or a member who can approve makes it (approver_required); a staff session needs its write lease. The body has no other member. If-Match carries the screen revision and is required (invalid_request when absent, revision_conflict when stale).

The dashboard session only; agents use PATCH /api/screens/{id} (updateScreen). On a protected screen an approver's assignment is a review decided by a second approver (202 pending_review).


Operation: patchScreenPlaylist.

- Authentication: dashboardSession
- Request body: ScreenPlaylistPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| id | path | required |  |
| If-Match | header | required | The screen's current revision, quoted or bare (the ETag of the last read). A stale revision is revision_conflict with current_revision. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| playlist_id | required | inline | The screen default playlist; null clears its own assignment. A takeover, campaign, schedule entry or inherited folder default can still show content; otherwise the paired Player shows its idle display. |

#### Request template

~~~sh
curl --request PATCH --cookie cookies.txt --cookie-jar cookies.txt --header 'If-Match: "REVISION"' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/playlist'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen as stored now; ETag carries its revision. | Screen (application/json); headers: ETag |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| 202 | The write became a review and waits for a person's approval (docs/reviews.md), under Propose access, or at any access level when it reaches a screen in a protected screen folder, where a second approver decides it (docs/screen-scope.md). A person approves, rejects or requests changes in the dashboard at review.url. An Idempotency-Key replay answers the same body. | ReviewPending (application/json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Rotate Screen Public Id**


Operation: rotateScreenPublicId.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/public-id/rotate'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Public URL credential rotated; player public-key enrollment preserved. Every runtime session is invalidated by the new content_access_generation and the live desired and active grants are reissued at that generation for their unchanged manifest revision, so a paired player that re-mints its session keeps authorizing the content it is already showing. | Screen (application/json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Get Screen Link**

The screen's link, the Player URL that opens this screen in a browser that keeps no storage, such as a vMix or OBS browser source. The url is a credential: below effective Manage it is empty and url_redacted is true. Needs the screens capability itself. screen_link_not_found when the screen has none.


Operation: getScreenLink.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/screens/RESOURCE_ID/link'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen's link. | ScreenLink (application/json); headers: Cache-Control |
| 404 | not_found for a screen outside the project; screen_link_not_found for a screen without a link. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/link

**Create Screen Link**

Issues the screen's link and returns its url, replacing any link the screen has (replaced says which). The screen must be active, paired once with its code (resource_conflict while it has no paired Player, screen_archived when archived).

A replacement bumps the screen's content_access_generation and reissues its live grants, as a public ID rotation does, so every session the old link opened ends at once; key-bound Players of the screen re-mint on their own. The first link bumps nothing. A link is not a device binding: the screen's keys are untouched.

Appends screen.link_changed (details.action created or replaced). If-Match is optional. The url is a credential: open it in one place.


Operation: createScreenLink.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/screens/RESOURCE_ID/link'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen's new link. | ScreenLink (application/json); headers: ETag, Cache-Control |
| 409 | resource_conflict (no paired Player yet) or screen_archived. | Problem (application/problem+json) |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Delete Screen Link**

Turns the screen's link off. The old url stops working at once and every session it opened ends, as in a replacement. Succeeds, changing nothing, when the screen has no link. Appends screen.link_changed (details.action deleted) when a link existed. If-Match is optional.


Operation: deleteScreenLink.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/screens/RESOURCE_ID/link'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Deleted, or there was none. | no body |
| 412 | revision_conflict. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/recovery/confirm

**Confirm Screen Recovery**

Confirms a pending screen recovery. A native pairing start that presented a host duid or serial matching exactly one screen in exactly one project, under a different bound public key, records an offer on that screen (Screen.recovery_pending, project event screen.recovery_offered).

The host object is a hint, never a credential: nothing is rebound until the owning project calls this route. A DUID is readable by any application on the panel, so an offer is a phishing surface.

It is mitigated by liveness (no offer while the bound key showed life in the last ten minutes), a limit of three offers per screen per rolling hour, and this same-project confirmation; recovery_pending.host and the event carry the offering device's platform, model, firmware, and manufacturer so the operator can compare them with the display in front of them before confirming.

Confirmation completes the pending native pairing onto the EXISTING screen: the pairing device receives pairing.claimed as usual and proves possession with pairing.complete, the new key becomes the screen identity, and the previous key retires with a fifteen-minute grace window in which its runtime sessions keep working and it may still mint, after which it is refused.

Playlists, label, timezone, schedules, and history are preserved. Same project only; another project's screen is not_found. If-Match is optional. Emits screen.recovered. recovery_not_offered when nothing is pending, recovery_expired when the pairing session lapsed, recovery_ambiguous when the identifier is attached in more than one place.

Problem responses never echo device identifiers.


Operation: confirmScreenRecovery.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to create or overwrite a key; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/recovery/confirm'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen recovered onto the pairing device's key; the response carries the current ETag. | Screen (application/json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 410 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/replace

**Replace Screen**

Moves the device paired as another screen of the project onto this one and archives that screen, so this screen keeps its name, tags, timezone, playlist, schedules, takeover, display schedule and override, video wall, comments and history on new hardware.

The device's public key (or browser credential) now opens this screen, which takes the replacement's host and Player; its health, status, storage, observation and queued commands start over. The key this screen held retires with the recovery grace window: it may still mint for fifteen minutes, then is refused (key_retired). Nothing is unbound.

The replaced screen is archived with archive_reason replaced and keeps no binding or host. Both screens advance content_access_generation, so every session either held ends; the device receives a player.reload addressed to the screen it left (details.reason replaced, valid for 24 hours) and mints again as this screen.

Both screens must be active with a paired Player (a pairing_pending screen or an unpaired replacement is resource_conflict, an archived one screen_archived); with names another screen of the project (invalid_request; another project's screen is not_found); a screen with a video wall needs a replacement Player that declared display.wall (wall_unsupported).

If-Match is optional and checks this screen's revision. Emits the project event screen.replaced (details.from_screen_id) and no screen.archived. An exact Idempotency-Key retry returns this same answer for twenty-four hours.

A caller limited to screens (docs/screen-scope.md) needs the screen and with inside its scope (outside_screen_scope; nothing is archived). A replace where either screen is protected is protected_target.


Operation: replaceScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenReplaceWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to create or overwrite a key; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| with | required | string | The id of the screen, in the same project, whose paired device takes over the screen in the path. It must be another active screen with a paired Player; it is archived with archive_reason replaced. minLength: 1 maxLength: 256 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/replace'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The device now opens this screen; the response is this screen with its new revision and the current ETag. | Screen (application/json) |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | resource_conflict, screen_archived, wall_unsupported, or protected_target when either screen is in a protected screen folder. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/archive

**Archive Screen**

Hides the screen from the default list and clears the live glass to the "Screen archived" status. Does not unbind the player public key. Releases screen_count quota. Cancels the screen's queued reboot, clear_cache, restart_app and update commands; a queued reload is still delivered. A screen in a protected screen folder is protected_target (409).


Operation: archiveScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/archive'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen archived. | Screen (application/json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/unarchive

**Unarchive Screen**

Restores an archived screen to the default list and remints a normal manifest. Must pass screen admission. Does not change the bound public key.

The manifest is issued again, so the screen's playlist is checked as an assignment is; a playlist that references missing or unready content answers resource_conflict naming the playlist, and the screen stays archived until the playlist is fixed.


Operation: unarchiveScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/unarchive'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen unarchived. | Screen (application/json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 413 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/reload

**Reload Screen**

Asks the Player of the named active or archived screen to reload (docs/player-compatibility.md §6.3). The optional body sets expires_in_s, how long the reload stays actionable (60 to 86400 seconds, default 86400, 24 hours); a value out of range is invalid_request naming expires_in_s.

A screen that is online is sent the durable per-screen player.reload event at once (details.reason project, a new reload_id, spread_s 0, expires_at) and the reload is recorded in Screen.commands as sent.

A screen that is offline holds the reload as queued with no event; when the backend applies the screen's next online transition before expires_at the event is appended, with the same reload_id and expires_at, and the command becomes sent. A newer reload supersedes an older one still queued.

A queued reload past expires_at reads expired, and POST /api/screens/{id}/commands/{command_id}/cancel cancels one still queued. The runtime SSE stream relays the event to every runtime session. A Player ignores a reload within ten minutes of the last one it acted on.

A web Player reloads at its next page boundary; a native Player remints, refetches its manifest, and checks for an update. The screen revision does not change. If-Match and Idempotency-Key are optional, as on archive; an exact Idempotency-Key retry returns the original body for twenty-four hours.

A pairing_pending screen has no Player yet and answers resource_conflict. Screen messages are exempt from the 1-credit API meter.


Operation: reloadScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenCommandWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| expires_in_s | optional | integer | How long the command stays actionable, in seconds from the request. A queued command is sent only before it expires; a Player ignores one after expires_at. default: 86400 minimum: 60 maximum: 86400 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/reload'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. state is sent when the reload went to the online screen's Player, queued when it waits for the screen to come online. The reload is actionable until expires_at. The ETag is the unchanged screen revision. | ScreenReloadAccepted (application/json); headers: Cache-Control, ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/playlist-schedule

**Get Screen Playlist Schedule**

Returns the screen's server-evaluated playlist schedule: entries in priority order (empty when the screen has none), updated_at, and the effective_playlist they currently produce. The ETag is the screen revision. pairing_pending, active, and archived screens are readable; another project's screen is not_found.


Operation: getScreenPlaylistSchedule.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/playlist-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Playlist schedule. | ScreenPlaylistScheduleView (application/json); headers: ETag |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PUT /api/screens/{id}/playlist-schedule

**Set Screen Playlist Schedule**

Replaces the screen's playlist schedule. The server evaluates it, never the Player: the first entry whose rule matches the screen's civil time (screenrig.schedule/v1 windows and from/until, in the screen timezone, including daylight-saving transitions and windows that cross midnight) is the effective playlist; when none matches, the assigned playlist_id is.

A takeover still wins over every entry. The screen must have a timezone and an assigned default playlist_id (invalid_request otherwise), so there is always something to fall back to.

Every referenced playlist is validated against the screen exactly as an assignment is (owned, ready references, page visibility needs a timezone, and a playlist with a format needs a screen whose wall has the same shape, else 409 playlist_format_mismatch), so a playlist the screen would refuse is refused here; the check writes the playlist document, so a concurrent playlist delete conflicts instead of racing it.

If a scheduled playlist later cannot be shown (for example its content no longer resolves), its entries are skipped at evaluation and screen.playlist_unavailable (warning, details playlist_id, entry_ids, code) is appended.

When the effective playlist changes the manifest is re-minted from it and the Player is signalled with screen.manifest_changed, as for an assignment, and screen.playlist_switched is appended. The screen revision is bumped (If-Match guards it) and screen.updated is appended with details.playlist_schedule_entries.

An archived screen answers screen_archived; its schedule, takeover, and default stay referenced (they block playlist delete) and are re-resolved when it is unarchived. Rate limits: screen-control-org 600, screen-control-screen 20, and screen-control-ip 60 per minute, shared with DELETE and the takeover routes.

On a protected screen the schedule is a review decided by a second approver (202 pending_review) at every access level. A new entry with paused true (a draft) is schedule change control, which the Enterprise plan includes; a project whose plan lacks it is refused with feature_not_in_plan (402).

Entries already stored may stay paused, and pausing a stored entry is open on every plan.


Operation: setScreenPlaylistSchedule.

- Authentication: projectBearer
- Request body: ScreenPlaylistScheduleWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| entries | required | array | Priority order: the first matching entry wins. DELETE clears the schedule. |

#### Request template

~~~sh
curl --request PUT --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/playlist-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The write became a review under Propose access and waits for a person's approval (docs/reviews.md). An Idempotency-Key replay answers the same body. | ReviewPending (application/json); headers: RateLimit-Policy, RateLimit |
| 200 | Screen with the stored schedule and its effective_playlist. The ETag is the new revision. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 402 | feature_not_in_plan — Schedule change control is part of the Enterprise plan; the problem names the feature, the plan the account is on and required_plan. An entry that locks a playlist to display 2 or beyond of a video wall (displays) other than the screen's stored entry is part of Plus. The problem names the feature, the project's plan and required_plan. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/screens/{id}/playlist-schedule

**Clear Screen Playlist Schedule**

Removes the screen's playlist schedule; the takeover, else a campaign that holds, else the assigned playlist, becomes effective, with the same manifest signalling as PUT. A screen without a schedule answers 200 unchanged (no revision bump, no event).

On a protected screen clearing the schedule is a review decided by a second approver (202 pending_review) at every access level.


Operation: clearScreenPlaylistSchedule.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/playlist-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The write became a review under Propose access and waits for a person's approval (docs/reviews.md). An Idempotency-Key replay answers the same body. | ReviewPending (application/json); headers: RateLimit-Policy, RateLimit |
| 200 | Screen without a playlist schedule. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/playlist-schedule/preview

**Preview Screen Playlist Schedule**

What replacing the screen's playlist schedule with entries would change, without making it.

The entries are checked as PUT checks them (grammar, the screen's timezone and displays, a default playlist, and each playlist as an assignment: it exists, has an approved version, fits the screen's wall and the region it covers, and its references resolve), so a preview refuses what the PUT refuses.

The answer says whether the screen would gain or lose levels of its schedule on its stack (a paused entry is on no stack, so publishing one gains it), and the screen's schedule calendar as it is (before) and as it would be (after) over from to to (default: a week from now; at most 93 days). Read access: nothing is written.


Operation: previewScreenPlaylistSchedule.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenSchedulePreviewRequest

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| entries | required | array |  |
| from | optional | string | Start of the calendars; defaults to now. format: "date-time" |
| to | optional | string | End of the calendars; defaults to a week after from, at most 93 days after it. format: "date-time" |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/playlist-schedule/preview'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The preview. | SchedulePreview (application/json) |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/playlist-schedule/revisions

**List Screen Playlist Schedule Revisions**

The screen's playlist schedule revisions, newest first: the whole entries list as stored after each write (a PUT, a DELETE, a restore, a schedule import), with the kind of change and who made it. A revision is numbered by the screen's revision at the write, so numbers have gaps where the screen changed otherwise.

A screen whose history has no row yet lists its schedule as it stands, with change baseline. Revisions go with the screen's purge. At most 50 per page; next_cursor while more remain, null on the last page.


Operation: listScreenPlaylistScheduleRevisions.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| after | query | optional | next_cursor of the previous page of the same list, with the same filters. Omitted starts at the newest row. A value that is not such a cursor is invalid_request. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/screens/RESOURCE_ID/playlist-schedule/revisions'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen's schedule revisions, newest first. | ScreenScheduleRevisionList (application/json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/playlist-schedule/revisions/{revision}/restore

**Restore Screen Playlist Schedule Revision**

Puts an earlier revision's entries back as the screen's schedule: the same write as PUT /api/screens/{id}/playlist-schedule with those entries (or DELETE when that revision had none), checked as they are checked now (a playlist deleted since refuses it), so under Propose access the restore becomes the same screen_change review the PUT becomes.

If-Match is required and names the screen's current revision. Earlier revisions stay as they were.


Operation: restoreScreenPlaylistScheduleRevision.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| revision | path | required | A revision of the record, as its revisions list numbers them. |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/screens/RESOURCE_ID/playlist-schedule/revisions/REVISION/restore'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The write became a review under Propose access and waits for a person's approval (docs/reviews.md). An Idempotency-Key replay answers the same body. | ReviewPending (application/json); headers: RateLimit-Policy, RateLimit |
| 200 | Screen with the restored schedule and its effective_playlist. The ETag is the new revision. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/takeover

**Set Screen Takeover**

Takes over the screen with one playlist ahead of campaigns, its playlist schedule and assigned playlist, until until (RFC 3339, strictly in the future and at most 7 days ahead) or, when until is null or omitted, until the takeover is cleared. reason is optional, at most 120 characters.

The screen must have an assigned default playlist_id (invalid_request otherwise). A takeover replaces an earlier one (screen.takeover_ended with reason replaced, or expired when the earlier one had already ended).

The playlist is validated as an assignment, including its format (a playlist with a format needs a screen whose wall has the same shape, else 409 playlist_format_mismatch). A takeover whose playlist later cannot be shown ends (screen.takeover_ended with reason playlist_deleted or playlist_unavailable).

Appends screen.takeover_started and, when the effective playlist changes, screen.playlist_switched plus the Player's screen.manifest_changed. When until passes, the boundary worker ends the takeover within 60 seconds (screen.takeover_ended with reason expired). The screen revision is bumped (If-Match guards it).

An archived screen answers screen_archived; unarchive re-resolves first, so a takeover that ended while archived ends then. The same screen-control rate limits as the schedule routes apply. On a protected screen a takeover is a review decided by a second approver (202 pending_review) at every access level.


Operation: setScreenTakeover.

- Authentication: projectBearer
- Request body: ScreenTakeoverWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| playlist_id | required | string | minLength: 1 |
| until | optional | inline | Strictly in the future and at most 7 days ahead. null or omitted holds the takeover until it is cleared. |
| reason | optional | string | Operator note carried on the takeover and its events. No control characters. maxLength: 120 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/takeover'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The write became a review under Propose access and waits for a person's approval (docs/reviews.md). An Idempotency-Key replay answers the same body. | ReviewPending (application/json); headers: RateLimit-Policy, RateLimit |
| 200 | Screen with the takeover and its effective_playlist. The ETag is the new revision. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Clear Screen Takeover**

Ends the takeover (screen.takeover_ended with reason cleared); the schedule, else the assigned playlist, becomes effective with the same manifest signalling. A screen without a takeover answers 200 unchanged (no revision bump, no event).

An archived screen's takeover is cleared too: it stays archived, its Player gets no signal, and unarchive resolves the effective playlist. On a protected screen clearing a takeover is a review decided by a second approver (202 pending_review) at every access level.


Operation: clearScreenTakeover.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Screenrig-Review-Note | header | optional | Optional proposer note on a review-class write, sent as percent-encoded UTF-8 (what JavaScript's encodeURIComponent writes). The server percent-decodes it (an invalid escape is 400 invalid_request); the decoded note is plain text, at most 280 characters after trimming, no control characters (else 400 invalid_request). Stored on the review when the write becomes one; ignored when it applies live. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/takeover'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The write became a review under Propose access and waits for a person's approval (docs/reviews.md). An Idempotency-Key replay answers the same body. | ReviewPending (application/json); headers: RateLimit-Policy, RateLimit |
| 200 | Screen without a takeover. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/tab-shares

**Start Tab Share**

Shows browser tabs on screens. Makes one temporary playlist of a full-canvas 1920x1080 iframe page per tab and takes every target screen over with it (reason "Shared browser tab") until until, at most 24 hours ahead (the default).

Targets are the union of screen_ids, every active screen in folder_ids and the folders inside them, and every active screen carrying any of tags; offline screens are included and show the share when they return.

A caller limited to screens (docs/screen-scope.md) reaches only the screens in its scope: a screen outside it is skipped (outside_screen_scope), and the schedule of the request is otherwise the same.

One tab stays up until the share ends; several advance after each tab's duration_s. reload_s reloads every page. qr adds a QR code of the page address, about 14% of the canvas height, in the bottom-right corner. cookies are the sender's sign-in for the pages: they are sealed at rest, never returned by any read, event, audit entry or revision, and served only in the runtime manifest of a screen showing the share (the iframe cookies member); a Player that cannot set them ignores them, and the web Player and the shells that hand off to it do.

A target that cannot be taken over (protected folder, no default playlist, archived, not found, format mismatch, outside scope, rate limited) is listed in skipped and does not fail the request. A screen already showing an earlier share is taken over by this one, and the earlier playlist is deleted once no screen shows it.

When no target is taken over the temporary playlist is deleted and the answer is the problem of the one target, or 409 resource_conflict naming the reasons. warnings lists tab addresses the backend could not embed (embedding_blocked: X-Frame-Options or frame-ancestors refuse the Player web app), checked once per address with a 3 second budget; it never blocks the share.

The screen-control rate limits apply per target screen (organization and screen), and the IP limit once per request. Every call is a new share; an Idempotency-Key replays nothing. The change is live: refused with insufficient_access under Propose access, and on the dashboard lane with approver_required to a member who cannot approve reviews.

On the dashboard lane the session's current project is used (no_project when none), and the request may come from the dashboard origin or from a Chrome or Firefox extension origin (chrome-extension://ID, moz-extension://UUID) with Sec-Fetch-Site absent, same-origin or none; a present X-ScreenRig-CSRF header must match csrf_token from GET /api/session.

Only this route, DELETE /api/tab-shares/{id}, POST /api/playlists/{id}/pages and POST /api/screens/{id}/screenshot take an extension origin.


Operation: startTabShare.

- Authentication: projectBearer OR dashboardSession
- Request body: TabShareWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| screen_ids | optional | array | Screens by id. Duplicates collapse. |
| folder_ids | optional | array | Screen folders; each includes every folder inside it. |
| tags | optional | array | A screen carrying any of the tags. |
| tabs | required | array |  |
| cookies | optional | array | The sender's cookies for the pages, at most 64 KiB encoded. Secrets; see POST /api/tab-shares. |
| reload_s | optional | integer | 0 (never, the default) or 60 to 86400: every page's iframe reloads this often. |
| until | optional | string | Strictly in the future and at most 24 hours ahead; 24 hours from now when omitted. The end of the takeover on every target and the expiry of the temporary playlist. format: "date-time" |
| qr | optional | boolean | When true every page carries a QR code of its address in the bottom-right corner. Default false. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/tab-shares'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The share. At least one screen was taken over. | TabShare (application/json); headers: RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/tab-shares/{id}

**Get Tab Share**

Reads a share: its end time and the screens whose takeover is still its playlist (a screen someone took over since, or that was archived, is not listed). 404 not_found once the share ended, expired or never was. A caller limited to screens (docs/screen-scope.md) sees only the screens in its scope. No extension-origin rule applies to a read.


Operation: getTabShare.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/tab-shares/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The share and the screens still showing it. | TabShareState (application/json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/tab-shares/{id}

**Stop Tab Share**

Ends a share: clears the takeover of every screen whose takeover is still its playlist (screen.takeover_ended with reason cleared, the schedule, else the assigned playlist, becoming effective with the same manifest signalling) and deletes the playlist. Allowed on screens in protected folders.

A screen someone took over since is left alone. 404 not_found when the share already ended; a client treats that as ended. One unit of each screen-control limit is spent per request. A caller limited to screens clears only the screens in its scope. The lanes, access rules and extension-origin rule are those of POST /api/tab-shares.


Operation: stopTabShare.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/tab-shares/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screens that were cleared. | TabShareStopped (application/json); headers: RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/play-page

**Play Screen Page**

Asks the screen's Player to start one page of the playlist it is playing now, so a screenshot taken next shows that page without waiting out the rotation (docs/player-compatibility.md §6.3.1).

The page must be a page of the screen's effective playlist, the one its manifest is built from: an active takeover, else a campaign that holds, else the current playlist schedule entry, else the assigned playlist. Otherwise 409 page_not_on_screen, whose detail names that playlist.

The screen's host hint must declare the play_page capability, else 409 play_page_unsupported and nothing is sent; update the Player. The screen must be active; an archived screen answers screen_archived and a pairing_pending one resource_conflict.

Appends the per-screen runtime command player.play_page (details page_id, playlist_id, manifest_revision, expires_at sixty seconds later; never delivered after expires_at). The Player starts the page from its beginning as if the rotation had reached it and continues the rotation from there. The screen revision does not change.

The Idempotency-Key is required, as for toast; an exact retry returns the original body for twenty-four hours. A refused request, a failed one, and an exact replay spend no rate-limit budget. Screen messages are exempt from the 1-credit API meter. A screen in a protected screen folder is protected_target (409).


Operation: playScreenPage.

- Authentication: projectBearer
- Request body: ScreenPlayPageWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Idempotency-Key | header | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| page_id | required | string | The id of a page of the playlist the screen is playing now (an id in that playlist's pages). minLength: 1 maxLength: 128 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/play-page'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. The command is actionable until expires_at. An exact idempotent retry returns this same body. | ScreenPlayPageAccepted (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/identify

**Identify Screen**

Asks the screen's Player to show the screen's name over its content for duration_ms (2000 to 600000, default 60000), so a person in front of several displays can tell which one a record is (docs/player-compatibility.md §6.3.2).

Appends the per-screen runtime command player.identify (details identify_id, label, screen_id, duration_ms, at, expires_at sixty seconds after at; never delivered after expires_at).

The Player draws a full-screen overlay above its content, the name very large and the screen id small below it, for duration_ms from receipt; playback continues underneath and a newer identify replaces the overlay.

The screen's host hint must declare the identify capability, else 409 identify_unsupported and nothing is sent; update the Player, or show the name with a toast. The screen must be active; an archived screen answers screen_archived and a pairing_pending one resource_conflict. The screen revision does not change.

The body and the Idempotency-Key are optional; an exact retry returns the original body for twenty-four hours. A refused request, a failed one, and an exact replay spend no rate-limit budget. Screen messages are exempt from the 1-credit API meter.


Operation: identifyScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenIdentifyWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| duration_ms | optional | integer | How long the overlay stays up, in milliseconds from the Player's receipt. Omitted values default to 60000. default: 60000 minimum: 2000 maximum: 600000 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/identify'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. The command is actionable until expires_at. An exact idempotent retry returns this same body. | ScreenIdentifyAccepted (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/suspend

**Suspend Screen**

Suspends the screen: its Player shows message instead of its content (docs/player-compatibility.md §6.9). The message is 1 to 280 characters after trimming; line feed is the only accepted control character.

Nothing else changes: the playlist, schedule, takeover, display control and every other setting stay and keep evolving, and resume shows what the screen would be showing now. Screen.suspension carries the message and since. Suspending a suspended screen replaces the message and keeps since.

The runtime manifest of a suspended screen has pages [] and a suspension member, and a manifest revision of its own, with or without an assigned playlist; the Player is told to refetch it at once. Appends the project-only screen.suspended (details message). An archived screen answers screen_archived. If-Match and Idempotency-Key are optional, as on archive.

A screen in a protected screen folder is protected_target (409).


Operation: suspendScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenSuspendWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| message | required | string | The notice the screen shows, 1 to 280 characters after trimming. Line feed is the only accepted control character; a Player wraps the text and keeps line feeds. minLength: 1 maxLength: 280 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/suspend'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen suspended. The ETag is the new screen revision. | Screen (application/json); headers: ETag |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/resume

**Resume Screen**

Ends the screen's suspension: its Player shows the screen's own content again, from the manifest the screen would be served now (docs/player-compatibility.md §6.9). The Player is told to refetch at once. Appends the project-only screen.resumed. Resuming a screen that is not suspended answers 200 with the screen unchanged: no revision bump and no event.

An archived screen answers screen_archived. No body. If-Match and Idempotency-Key are optional, as on archive.


Operation: resumeScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/resume'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen resumed, or not suspended. The ETag is the screen revision. | Screen (application/json); headers: ETag |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/reboot

**Reboot Screen**

Asks the screen's Player to reboot the device (docs/player-compatibility.md §6.5). The optional body sets expires_in_s, how long the reboot stays actionable (60 to 86400 seconds, default 86400, 24 hours); a value out of range is invalid_request naming expires_in_s. Always appends the project-only screen.reboot_requested.

A screen that is online is sent the per-screen runtime command player.reboot at once (details reboot_id, reason project, at, expires_at; never delivered after expires_at) and the reboot is recorded in Screen.commands as sent.

A screen that is offline holds the reboot as queued with no event; when the backend applies the screen's next online transition before expires_at the event is appended, with the same reboot_id and expires_at, and the command becomes sent. A sent reboot becomes done when a health report carrying uptime_s puts the boot after it was sent.

A newer reboot supersedes an older one still queued. A queued reboot past expires_at reads expired, and POST /api/screens/{id}/commands/{command_id}/cancel cancels one still queued. The screen must be active; an archived screen answers screen_archived and a pairing_pending one resource_conflict.

The screen's host hint must declare the reboot capability, else 409 reboot_unsupported and nothing is recorded. The screen revision does not change. If-Match and Idempotency-Key are optional; an exact retry returns the same body for twenty-four hours.

At most 2 per screen per 10 minutes; a refused request (reboot_unsupported, not_found, a conflict), a failed one, and an exact Idempotency-Key replay spend nothing.

From the moment the reboot is sent until a health report proves it done or ten minutes pass, the screen raises no screen_offline, display_disconnected or crashing notice or alert; a condition that still holds then opens normally. The reboot schedule (PUT /api/screens/{id}/reboot-schedule) issues the same reboot with source schedule.


Operation: rebootScreen.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenCommandWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| expires_in_s | optional | integer | How long the command stays actionable, in seconds from the request. A queued command is sent only before it expires; a Player ignores one after expires_at. default: 86400 minimum: 60 maximum: 86400 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/reboot'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. state is sent when the reboot went to the online screen's Player, queued when it waits for the screen to come online. The reboot is actionable until expires_at. The ETag is the unchanged screen revision. | ScreenRebootAccepted (application/json); headers: Cache-Control, ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/clear-cache

**Clear Screen Cache**

Asks the screen's Player to clear its caches (docs/player-compatibility.md §6.5.1). The Player deletes its downloaded media, application packages and data caches, keeps its pairing, identity and settings, then fetches the manifest and content again.

The optional body sets expires_in_s, how long the command stays actionable (60 to 86400 seconds, default 86400, 24 hours); a value out of range is invalid_request naming expires_in_s. Always appends the project-only screen.command_requested.

A screen that is online is sent the per-screen runtime command player.clear_cache at once (details command_id, command, reason project, at, expires_at; never delivered after expires_at) and the command is recorded in Screen.commands as sent.

A screen that is offline holds the command as queued with no event; when the backend applies the screen's next online transition before expires_at the event is appended, with the same command_id and expires_at, and the command becomes sent.

The Player reports how it ended: the command becomes done or failed in Screen.commands with the cause the Player reported (cause, detail), and the project-only screen.command_result is appended. A sent command with no report stays sent.

A newer command of the same type supersedes one still queued, and POST /api/screens/{id}/commands/{command_id}/cancel cancels one still queued. The screen must be active; an archived screen answers screen_archived and a pairing_pending one resource_conflict.

The screen's host hint must declare the clear_cache capability, else 409 command_unsupported and nothing is recorded. The screen revision does not change. If-Match and Idempotency-Key are optional; an exact retry returns the same body for twenty-four hours.

At most 2 per screen per 10 minutes across clear-cache, restart-app and update; a refused request (command_unsupported, not_found, a conflict), a failed one, and an exact Idempotency-Key replay spend nothing.


Operation: clearScreenCache.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenCommandWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| expires_in_s | optional | integer | How long the command stays actionable, in seconds from the request. A queued command is sent only before it expires; a Player ignores one after expires_at. default: 86400 minimum: 60 maximum: 86400 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/clear-cache'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. state is sent when the command went to the online screen's Player, queued when it waits for the screen to come online. The command is actionable until expires_at. The ETag is the unchanged screen revision. | ScreenMaintenanceAccepted (application/json); headers: Cache-Control, ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/restart-app

**Restart Screen App**

Asks the screen's Player to restart its application, not the device (docs/player-compatibility.md §6.5.1). The Player process restarts and reports from the new process. The optional body sets expires_in_s, how long the command stays actionable (60 to 86400 seconds, default 86400, 24 hours); a value out of range is invalid_request naming expires_in_s.

Always appends the project-only screen.command_requested. A screen that is online is sent the per-screen runtime command player.restart_app at once (details command_id, command, reason project, at, expires_at; never delivered after expires_at) and the command is recorded in Screen.commands as sent.

A screen that is offline holds the command as queued with no event; when the backend applies the screen's next online transition before expires_at the event is appended, with the same command_id and expires_at, and the command becomes sent.

The Player reports how it ended: the command becomes done or failed in Screen.commands with the cause the Player reported (cause, detail), and the project-only screen.command_result is appended. A sent command with no report stays sent.

A newer command of the same type supersedes one still queued, and POST /api/screens/{id}/commands/{command_id}/cancel cancels one still queued. The screen must be active; an archived screen answers screen_archived and a pairing_pending one resource_conflict.

The screen's host hint must declare the restart_app capability, else 409 command_unsupported and nothing is recorded. The screen revision does not change. If-Match and Idempotency-Key are optional; an exact retry returns the same body for twenty-four hours.

At most 2 per screen per 10 minutes across clear-cache, restart-app and update; a refused request (command_unsupported, not_found, a conflict), a failed one, and an exact Idempotency-Key replay spend nothing.


Operation: restartScreenApp.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenCommandWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| expires_in_s | optional | integer | How long the command stays actionable, in seconds from the request. A queued command is sent only before it expires; a Player ignores one after expires_at. default: 86400 minimum: 60 maximum: 86400 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/restart-app'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. state is sent when the command went to the online screen's Player, queued when it waits for the screen to come online. The command is actionable until expires_at. The ETag is the unchanged screen revision. | ScreenMaintenanceAccepted (application/json); headers: Cache-Control, ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/update

**Update Screen Player**

Asks the screen's Player to check for the newest Player release now and install it (docs/player- compatibility.md §6.5.1). The result cause is up_to_date when the newest release already runs and updated when a release was installed; a store-managed or device-managed install declares no update capability.

The optional body sets expires_in_s, how long the command stays actionable (60 to 86400 seconds, default 86400, 24 hours); a value out of range is invalid_request naming expires_in_s. Always appends the project-only screen.command_requested.

A screen that is online is sent the per-screen runtime command player.update at once (details command_id, command, reason project, at, expires_at; never delivered after expires_at) and the command is recorded in Screen.commands as sent.

A screen that is offline holds the command as queued with no event; when the backend applies the screen's next online transition before expires_at the event is appended, with the same command_id and expires_at, and the command becomes sent.

The Player reports how it ended: the command becomes done or failed in Screen.commands with the cause the Player reported (cause, detail), and the project-only screen.command_result is appended. A sent command with no report stays sent.

A newer command of the same type supersedes one still queued, and POST /api/screens/{id}/commands/{command_id}/cancel cancels one still queued. The screen must be active; an archived screen answers screen_archived and a pairing_pending one resource_conflict.

The screen's host hint must declare the update capability, else 409 command_unsupported and nothing is recorded. The screen revision does not change. If-Match and Idempotency-Key are optional; an exact retry returns the same body for twenty-four hours.

At most 2 per screen per 10 minutes across clear-cache, restart-app and update; a refused request (command_unsupported, not_found, a conflict), a failed one, and an exact Idempotency-Key replay spend nothing.


Operation: updateScreenPlayer.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenCommandWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| expires_in_s | optional | integer | How long the command stays actionable, in seconds from the request. A queued command is sent only before it expires; a Player ignores one after expires_at. default: 86400 minimum: 60 maximum: 86400 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/update'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. state is sent when the command went to the online screen's Player, queued when it waits for the screen to come online. The command is actionable until expires_at. The ETag is the unchanged screen revision. | ScreenMaintenanceAccepted (application/json); headers: Cache-Control, ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**List Screen Commands**

Lists every reboot and reload asked of an active or archived screen, newest first: the commands Screen.commands holds (its newest 10) and every older one, with the state each reached. A queued command past expires_at reads expired. A command that was still queued when the screen's device was replaced reads cancelled.

Cancel one still queued with POST /api/screens/{id}/commands/{command_id}/cancel.


Operation: listScreenCommands.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| cursor | query | optional | The next_cursor of the previous page. |
| limit | query | optional |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/commands'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | One page of the screen's commands. | ScreenCommandPage (application/json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/commands/{command_id}/cancel

**Cancel Screen Command**

Cancels a reboot or reload that is still queued for the screen, so it is never sent (docs/player-compatibility.md §6.3, §6.5). command_id is an id in Screen.commands, or the reboot_id or reload_id of the 202 answer. Only a command whose state is queued can be cancelled; it becomes cancelled.

A command already sent (sent, done) answers 409 resource_conflict "The command was already sent."; one that is no longer queued (expired, cancelled, superseded) answers 409 resource_conflict "The command is no longer queued."; an id the screen does not list answers 404 not_found. The screen's list keeps its newest 10 commands.

The screen revision does not change. If-Match and Idempotency-Key are optional; an exact retry returns the same screen for twenty-four hours.


Operation: cancelScreenCommand.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| command_id | path | required | The id of one of the screen's commands: the id in Screen.commands, the reboot_id or reload_id of the 202 answer. |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/commands/COMMAND_ID/cancel'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen, with the command cancelled. The ETag is the unchanged screen revision. | Screen (application/json); headers: ETag |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/display

**Set Screen Display**

Requests display on or off now: a manual override of the display schedule (docs/player-compatibility.md §6.5). power is on or off. until (RFC 3339, strictly future, at most 7 days ahead) ends it; without until it ends at the display schedule's next boundary when a schedule is enabled, else it holds until replaced.

Stores the override (Screen.display.override), re-mints the manifest (its display member carries the override so an offline Player keeps it), appends the runtime command player.display (details override_id, power, until, at, expires_at) and the project-only screen.display_changed, and bumps the screen revision (If-Match guards it). The screen must be active.

What the Player achieved is reported back in its health display.power (Screen.display.reported). Success saves intent without waiting for a report; Screen.display.override_status compares subsequent observations and Screen.display.control names the advertised control method.

A scrim-only Player removes its overlay on an on request without a hardware wake command.

Without until and without a schedule boundary inside the next eight days (no enabled schedule, or none that changes), the override holds until DELETE /api/screens/{id}/display or a replacement; a later display-schedule PUT or DELETE, or a timezone change, re-bounds an until-less override to the new next boundary.

A refused or failed request, and an exact Idempotency-Key replay, spend no budget. A screen in a protected screen folder is protected_target (409).


Operation: setScreenDisplay.

- Authentication: projectBearer OR dashboardSession
- Request body: ScreenDisplayWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| power | required | string | Allowed: "on", "off" |
| until | optional | inline | Strictly future, at most 7 days ahead. Omitted or null: until the display schedule's next boundary, else until replaced. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/display'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen with its display state. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Clear Screen Display**

Ends the manual display override so the display schedule (else on) applies again. Re-mints the manifest (its display member drops the override; the Player follows screen.manifest_changed), appends screen.display_changed with change override_cleared, and bumps the revision. A screen without an active override answers 200 unchanged.

A screen in a protected screen folder is protected_target (409).


Operation: clearScreenDisplay.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/display'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen with its display state. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/display-schedule

**Get Screen Display Schedule**

Returns display_schedule (null when none) and the screen's display state. The ETag is the screen revision.


Operation: getScreenDisplaySchedule.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/display-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Display schedule. | ScreenDisplayScheduleView (application/json); headers: ETag |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PUT /api/screens/{id}/display-schedule

**Set Screen Display Schedule**

Replaces the display schedule: enabled and 1 to 16 screenrig.schedule/v1 windows (days, start/end HH:MM, end <= start crosses midnight owned by the start day, whole day without edges) when the display is ON; outside every window the Player puts the display to standby.

Evaluated on the device in the screen timezone (required, invalid_request otherwise), so it keeps working offline: the manifest's display member carries it. enabled false keeps the windows and leaves the display on. Re-mints the manifest, appends screen.display_changed, bumps the revision (If-Match guards it). The screen must be pairing_pending or active.

A screen with no manifest yet (never minted, or no playlist) stores the schedule and its first manifest carries it; a screen without a playlist applies it once a playlist is assigned. An until-less override is re-bounded to the new schedule's next boundary. A screen in a protected screen folder is protected_target (409).


Operation: setScreenDisplaySchedule.

- Authentication: projectBearer
- Request body: ScreenDisplayScheduleWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| enabled | required | boolean |  |
| windows | required | array | ON windows. |

#### Request template

~~~sh
curl --request PUT --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/display-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen with its display state. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/screens/{id}/display-schedule

**Clear Screen Display Schedule**

Removes the display schedule (the display stays on unless overridden). Clearing none answers 200 unchanged. A screen in a protected screen folder is protected_target (409).


Operation: clearScreenDisplaySchedule.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/display-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen with its display state. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope as on OutsideScreenScope, or approver_required - a member limited to screens who cannot approve reviews does not change what a screen plays or when (docs/screen-scope.md). | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/reboot-schedule

**Get Screen Reboot Schedule**

Returns reboot_schedule (null when none) and next_reboot_at, the start of the next window the backend reboots the screen at (null when there is none). The ETag is the screen revision.


Operation: getScreenRebootSchedule.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/reboot-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Reboot schedule. | ScreenRebootScheduleView (application/json); headers: ETag |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PUT /api/screens/{id}/reboot-schedule

**Set Screen Reboot Schedule**

Replaces the reboot schedule: enabled and 1 to 16 screenrig.schedule/v1 windows (days, start/end HH:MM, end <= start crosses midnight owned by the start day, whole day without edges), each 5 minutes to 24 hours long.

At the start of each window in the screen timezone (required, invalid_request otherwise) the backend issues the same reboot POST /api/screens/{id}/reboot does, with source schedule in Screen.commands, screen.reboot_requested and the player.reboot details.

A start inside a spring-forward gap fires at the first valid minute of the window; a window with no valid minute that day, because it lies wholly inside the gap, is skipped with screen.reboot_skipped reason window_missed (next_reboot_at shows the minute the gap ends). A fall-back repeat fires once per window per local date.

A screen that is online is sent the reboot at once; an offline one keeps it queued only until its window ends (expires_at), so a late reboot never lands outside it. An occurrence never replaces a reboot a person asked for that still waits for the screen: it is skipped with screen.reboot_skipped reason reboot_pending.

A timezone change cancels the scheduled reboots still waiting and moves next_reboot_at to the new zone.

A reboot, scheduled or asked for, raises no screen_offline, display_disconnected or crashing alert for ten minutes after it is sent, and a notice for one of them that falls due in that time waits until the period ends plus its hold (a crashing notice is skipped as planned_reboot). enabled false keeps the windows and reboots nothing.

Time windows that leave no gap, so that no start follows, are invalid_request. The screen's host hint must declare the reboot capability, else 409 reboot_unsupported and nothing is stored; a screen that loses it later skips each window with screen.reboot_skipped and keeps its schedule.

Appends screen.reboot_schedule_changed, bumps the revision (If-Match guards it), and never re-mints the manifest. The screen must be active.


Operation: setScreenRebootSchedule.

- Authentication: projectBearer
- Request body: ScreenRebootScheduleWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| enabled | required | boolean |  |
| windows | required | array | Reboot windows: the backend reboots the screen at the start of each, in the screen timezone. Each is 5 minutes to 24 hours long. |

#### Request template

~~~sh
curl --request PUT --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/reboot-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen with its reboot schedule and next_reboot_at. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 402 | feature_not_in_plan — Scheduled reboots are part of Plus; enabled true needs it, enabled false and DELETE do not. The problem names the feature, the project's plan and required_plan. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/screens/{id}/reboot-schedule

**Clear Screen Reboot Schedule**

Removes the reboot schedule and next_reboot_at; a reboot already issued is not cancelled (POST /api/screens/{id}/commands/{command_id}/cancel cancels one still queued). Clearing none answers 200 unchanged.


Operation: clearScreenRebootSchedule.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://screenrig.ai/api/screens/RESOURCE_ID/reboot-schedule'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screen without a reboot schedule. | Screen (application/json); headers: ETag, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/toast

**Post Screen Toast**

Posts one transient stage-chrome toast to the named screen. A toast is not a primitive: it does not occupy a canvas slot, has no layer, and never participates in readiness or crossfade. The durable event type is screen.toast on the existing runtime SSE stream. Runtime scan never delivers a toast after expires_at, so a persisted cursor cannot replay it later.

Level colours are player chrome and are not API fields. Text is 1 to 120 characters, line feed is the only accepted line break, and at most three lines are accepted; a Player shows a toast on one line, a line feed as a space.

Recognizable ScreenRig credential material and other control characters are rejected. duration_ms defaults to 60000 and must be between 2000 and 60000 inclusive. An exact Idempotency-Key retry returns the original expires_at for twenty-four hours. Latest-wins: there is no queue and no cancel verb. Screen messages are exempt from the 1-credit API meter.

A screen in a protected screen folder is protected_target (409).


Operation: postScreenToast.

- Authentication: projectBearer
- Request body: ScreenToastWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Idempotency-Key | header | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| level | required | string | Closed severity token. Player chrome chooses the fill; the API never carries a colour. Allowed: "error", "alert", "info" |
| text | required | string | 1 to 120 characters. Line feed is the only accepted line break. At most three lines; a Player shows the toast on one line, a line feed as a space. Other control characters and recognizable ScreenRig credential material are rejected. minLength: 1 maxLength: 120 |
| duration_ms | optional | integer | Visible duration in milliseconds. Omitted values default to 60000. default: 60000 minimum: 2000 maximum: 60000 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Idempotency-Key: REQUEST_ID' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/toast'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. The toast is durable until expires_at. An exact idempotent retry returns this same expiry. | ScreenToastAccepted (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | protected_target when the screen is in a protected screen folder (docs/screen-scope.md; clear the folder's protection through a review, make the change, protect it again), or the other conflicts of the operation. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/storage-forecast

**Forecast Screen Storage**

Pre-assignment fit dry run: answers whether one playlist would fit the named screen before it is assigned, with the same target selection as Screen.storage_forecast (the screen's last reported capacity, plan A.3 only, no transition prediction, nothing treated as local).

It writes nothing: no assignment, no screen revision, no event, and the screen's storage_forecast read model is untouched. The project bearer is authoritative; a screen or playlist of another project is not_found. fit is unknown when the screen has no storage report, or when the playlist's content references are not ready, and both byte counts are null then.

An optional playlist_revision refuses a playlist that changed since the caller read it with revision_conflict. A report older than 24 hours is stale but still forecast from, exactly like Screen.storage_forecast.


Operation: forecastScreenStorage.

- Authentication: projectBearer
- Request body: ScreenStorageForecastRequest

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| playlist_id | required | string | minLength: 1 |
| playlist_revision | optional | integer | Optional expected playlist revision. A different stored revision is revision_conflict and no forecast is returned. minimum: 1 |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/storage-forecast'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Dry-run forecast. fit unknown means there is nothing to forecast from; the byte counts and received_at are null. | ScreenStorageForecastDryRun (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 412 | revision_conflict — playlist_revision is not the playlist's current revision; no forecast is returned. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Explain Screen Campaigns**

Why each campaign of the project plays on this screen or not, in precedence order, with the screen's effective playlist. items and effective_playlist are the screen's state now.

With at (and optionally to), at is the same resolver the boundary sweep runs, asked about that instant, with a verdict for the takeover, every campaign and every schedule entry (won, outranked, not_started, ended, outside_hours, off), the playlist at that instant, and what the answer does not judge.

With to as well, at.timeline lists the spans between at and to in which the playlist does not change. The schedule, takeover and campaigns are read as they are now.


Operation: explainScreenCampaigns.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| at | query | optional | The instant to explain, either RFC 3339 with a zone (2026-03-08T09:30:00Z or 2026-03-08T01:30:00-08:00) or a time on the screen's own clock (2026-03-08T09:30, optionally with seconds). A clock time that daylight saving skips or repeats is refused with invalid_request naming at, and a repeated one names the two instants to choose from. Defaults to now when only to is given. |
| to | query | optional | End of the timeline of switches that starts at at, in the same two forms as at. Later than at, with no limit on how far; one answer lists at most 500 spans, a stretch of eight days with no change counting as one, and timeline_truncated says it stopped before reaching to. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/screens/RESOURCE_ID/campaigns'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Every campaign's standing on the screen. | ScreenCampaigns (application/json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Get Screen Screenshot**

Returns the current ready still WebP for the named screen. Optional capture_id must match that ready slot. A named capture_id that expired with no upload is screenshot_unavailable. A named capture_id replaced by a later request is resource_conflict. Image bytes are never a signed URL or object key. Range is not accepted.

Screen messages are exempt from the 1-credit API meter. The ETag is the strong SHA-256 of the stored WebP. A dashboard session response is private, no-cache, and a dashboard If-None-Match that matches the current image, checked after the screen and capture resolve in the session's project, returns 304 without opening the stored bytes.

A project bearer response stays private, no-store.


Operation: getScreenScreenshot.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| capture_id | query | optional |  |
| If-None-Match | header | optional | Strong ETag of a screenshot this route returned. Honored for a dashboard session. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/screenshot'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Current ready still WebP. | string (image/webp); headers: Content-Type, Content-Length, ETag, X-ScreenRig-Capture-ID, X-ScreenRig-Captured-At, Cache-Control, Content-Disposition |
| 304 | Dashboard session only. The current image's ETag matches If-None-Match. No body is produced and object storage is not read. The capture headers name the current capture, which can differ from the stored response's when a later capture encoded to the same bytes. | no body; headers: ETag, Cache-Control, X-ScreenRig-Capture-ID, X-ScreenRig-Captured-At |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/screenshot

**Request Screen Screenshot**

Requests one still screenshot of the named active screen. Latest-wins: a new request replaces any in-flight capture_id and does not 409. The current ready slot is kept until a later upload succeeds. capture_id is an optional environment prefix, then shot_ and a random suffix. expires_at is thirty seconds after accept.

The durable event type is screen.screenshot_requested on the existing runtime SSE stream; runtime scan never delivers it after expires_at. Event details name capture_id, expires_at, max_width_divisor 2, max_height_divisor 2, format image/webp, quality 80, and max_bytes 2097152. Image bytes never appear on SSE.

Prepaid remaining credit is not a gate, and this screen-message path is exempt from the 1-credit API meter. An exact Idempotency-Key retry returns the original capture_id and expires_at for twenty-four hours.

On the dashboard lane the request may come from a Chrome or Firefox extension origin as POST /api/tab-shares may, to confirm a shared tab (GET /api/screens/{id}/screenshot/status and GET /api/screens/{id}/screenshot need no origin rule).


Operation: requestScreenScreenshot.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| Idempotency-Key | header | required |  |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Idempotency-Key: REQUEST_ID' 'https://screenrig.ai/api/screens/RESOURCE_ID/screenshot'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | Accepted. The request is durable until expires_at. An exact idempotent retry returns this same capture_id and expiry. | ScreenScreenshotAccepted (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/screenshot/status

**Get Screen Screenshot Status**

Returns the screenshot state machine for the named screen when it exists for this project. idle has no request and no ready slot. pending is the current capture_id before expires_at with no upload. ready means the last successful upload matches the current capture_id. timed_out means the current id reached expires_at with no upload.

Timeout is lazy: the first status GET or next POST after expiry records it and appends screen.screenshot_failed once with reason expired. A timed-out request does not delete an older ready slot. Screen messages are exempt from the 1-credit API meter.


Operation: getScreenScreenshotStatus.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/screenshot/status'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Screenshot status for this project's screen. | ScreenScreenshotStatus (application/json); headers: Cache-Control |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Get Screen Live**

Returns the screen's live session after lazy expiry: idle when it has none, requested while the Player has been asked to publish, publishing once the media relay holds its video, ended with ended_reason (stopped, expired, consent_denied, consent_timeout, capture_failed, connection_failed or player_stopped; an open enum) for ten minutes after it ended. publisher.connected is true only while the Player's last report, at most 45 seconds old, said it is connected; it is the Player's own word, not proof of picture. 404 feature_unavailable when the server has no media relay configured.


Operation: getScreenLive.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/live'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The screen's live session. | LiveSession (application/json); headers: Cache-Control |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/live

**Start Screen Live**

Starts live view of the named screen, or renews the one it has (docs/player-compatibility.md §6.10): sub-second video of what the screen plays, from the Player through a media relay to the dashboard. A screen has one live session, shared by every viewer.

Its lease runs duration_s from now (30 to 300, default 300) and a start or renewal sets lease_until to the later of its current end and that instant, never past max_until, five minutes after the session began. The session ends by itself at the latest then: nothing renews or restarts it, and a new session needs a new start.

It ends earlier when the lease passes with no renewal, on DELETE, or when the Player ends it; the Player stops publishing within about 20 seconds of the lease ending.

With live_id the request only renews that session: when it is not the screen's live session (it ended, or the screen started another) the answer is 409 live_unavailable naming ended_reason, and no session starts or is extended.

A new session appends the per-screen runtime command player.live_publish (details live_id, at, expires_at sixty seconds later, max_until; never delivered after expires_at); a renewal sends it again only when the Player never joined and the last command expired.

The screen must be active and online and its host hint must declare the live_view capability: 409 live_unsupported (update the Player, or use a screenshot), 409 live_unavailable (offline), screen_archived. 404 feature_unavailable when the server has no media relay configured. 201 is a new session, 200 a renewed one; both return the LiveSession with a viewer_url that opens the dashboard viewer.

State requested lasts until the Player publishes: poll GET until publisher.connected. Live view is free and exempt from the 1-credit API meter, like a screenshot.


Operation: startScreenLive.

- Authentication: projectBearer OR dashboardSession
- Request body: LiveStartRequest

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| duration_s | optional | integer | How long the lease runs from now, in seconds. A session lasts five minutes at most, whatever the lease. default: 300 minimum: 30 maximum: 300 |
| live_id | optional | string | Renew this session only. When it is not the screen's live session, the answer is live_unavailable (the detail names ended_reason) and nothing starts or is extended. Without it, a start begins a session when the screen has none. pattern: "^lv_[a-z2-7]{26}$" |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/live'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | A new live session; the Player was asked to publish. | LiveSession (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 200 | The screen's live session, renewed. | LiveSession (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Stop Screen Live**

Ends the screen's live session (ended_reason stopped), appends player.live_stop (details live_id, at, expires_at sixty seconds later), and closes its tracks on the media relay. 204 also when the screen has no live session. A Player that misses the command stops at its next heartbeat. 404 feature_unavailable when the server has no media relay configured.


Operation: stopScreenLive.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/live'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Stopped, or there was nothing to stop. | no body; headers: Cache-Control |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/live/viewers

**Create Screen Live Viewer**

Opens a viewer of the screen's live session: a new media relay session that subscribes to the Player's video, and the relay's WebRTC offer.

Apply offer with setRemoteDescription on an RTCPeerConnection built with ice_servers (short-lived credentials), create the answer, wait for ICE gathering to complete or two seconds, then PUT the answer to /api/screens/{id}/live/viewers/{viewer_id} within two minutes.

Candidates travel inside the SDP; there is no separate candidate call. 409 live_not_ready while the session is requested, 409 live_unavailable when the screen has no live session or it ended (the detail names ended_reason). 404 feature_unavailable when the server has no media relay configured.


Operation: createScreenLiveViewer.

- Authentication: projectBearer OR dashboardSession
- Request body: object

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/live/viewers'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | A viewer and the relay's offer. | LiveViewer (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PUT /api/screens/{id}/live/viewers/{viewer_id}

**Answer Screen Live Viewer**

Applies the viewer's WebRTC answer to the relay's offer. A viewer answers once, within two minutes of its offer; an unknown, expired, already answered or another screen's viewer_id is 404 not_found. 204 once the relay accepts the answer; media then flows from the relay to the viewer. 404 feature_unavailable when the server has no media relay configured.


Operation: answerScreenLiveViewer.

- Authentication: projectBearer OR dashboardSession
- Request body: LiveViewerAnswer

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| viewer_id | path | required |  |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| answer | required | SessionDescription |  |

#### Request template

~~~sh
curl --request PUT --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://screenrig.ai/api/screens/RESOURCE_ID/live/viewers/VIEWER_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | The relay accepted the answer. | no body; headers: Cache-Control |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/screens/{id}/live/frames

**Start Screen Live Frames**

Starts JPEG frames of the screen's live session, for an agent that has HTTPS and no WebRTC: the media relay decodes the Player's published video and delivers one JPEG about every second, and GET /api/screens/{id}/live/frame returns the latest.

The session must be publishing (409 live_not_ready until then, 409 live_unavailable when there is none or it ended; start one with POST /api/screens/{id}/live).

Frames are started once per session: asking again answers the same adapter's progress, and a Player that reconnects ends its frames, so ask again. state is starting until the first frame arrives, streaming while frames arrive, and silent when the adapter is older than fifteen seconds and none has arrived in that time (last_frame_at, when present, says when frames last came).

The frames end with the session. 404 feature_unavailable when the server has no media relay or no frames origin configured. Live frames are free and exempt from the 1-credit API meter, like a screenshot. They are what the Player's encoder sent, scaled and lossy; they do not replace a screenshot as proof of layout.


Operation: startScreenLiveFrames.

- Authentication: projectBearer
- Request body: object

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/live/frames'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 202 | The frame adapter, and how far it has come. | LiveFrames (application/json); headers: Cache-Control, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/live/frame

**Get Screen Live Frame**

Returns the latest JPEG frame of the screen's live session.

Without after it returns the latest frame there is; with after (the ScreenRig-Captured-At of the frame you already have) it returns the first frame captured later, waiting up to five seconds for one, so a loop that passes each answer's ScreenRig-Captured-At back as after receives every new frame once.

ScreenRig-Captured-At is when the backend received the frame.

When no new frame comes in time the answer is 409 live_not_ready with Retry-After: 1; 409 resource_conflict answers a session whose frames were not started (POST /api/screens/{id}/live/frames first, and again after the Player reconnects); 409 live_unavailable answers a session that has ended or whose adapter is silent.

Frames are relay-decoded JPEGs of the live stream, at most 1280 by 720: they show what the Player encoded and sent, not a screenshot. They end with the session (max_until at the latest). 404 feature_unavailable when the server has no media relay or no frames origin configured. Free and exempt from the 1-credit API meter.


Operation: getScreenLiveFrame.

- Authentication: projectBearer
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| after | query | optional | Return a frame captured after this instant (RFC 3339, as in ScreenRig-Captured-At). |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' 'https://api.screenrig.ai/api/screens/RESOURCE_ID/live/frame'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The frame. | string (image/jpeg); headers: Content-Type, Content-Length, ETag, Cache-Control, ScreenRig-Live-Id, ScreenRig-Captured-At, ScreenRig-Frame-Width, ScreenRig-Frame-Height, RateLimit-Policy, RateLimit |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 409 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 429 | Stable rate_limited problem. Retry only after the authoritative Retry-After interval. Fixed-window denials also include the applicable RateLimit-Policy and RateLimit members; concurrency and byte-budget denials omit those optional disclosures. | Problem (application/problem+json); headers: Retry-After, RateLimit-Policy, RateLimit |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /live/frames

**Connect Live Frames**

The media relay's frame adapter connects here with a WebSocket upgrade; no client, agent or Player calls it. The token is the credential: POST /api/screens/{id}/live/frames hands it to the relay inside the adapter's endpoint and stores only its SHA-256.

Each binary message the relay sends is one protobuf Packet (field 1 sequenceNumber, field 2 timestamp, field 5 payload) whose payload is one whole JPEG of at most 2 MiB; anything else is skipped. The backend keeps the latest frame for fifteen seconds. One connection per token: a newer one replaces the older.

The connection is closed with 1000 when the live session ends, and a token of an ended session is refused with 401 before the upgrade. 404 feature_unavailable when the server has no frames origin configured.


Operation: connectLiveFrames.

- Authentication: none
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| token | query | required | The adapter's token, 43 characters. |

#### Request template

~~~sh
curl --request GET 'https://api.screenrig.ai/live/frames?token=TOKEN'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 101 | Switching Protocols to WebSocket. | no body |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/screens/{id}/health/history

**Get Screen Health History**

The health a screen reported over time. resolution=raw (default) returns each accepted report in time order, shaped like Screen.health without stale; resolution=hour returns one summary per UTC hour (reports, peak and average temperature, peak load and memory ratio, weakest Wi-Fi signal, whether the display was disconnected, peak crash and renderer-restart counts). from (inclusive) and to (exclusive) default to the last 24 hours; the range is at most 31 days for raw and 93 days for hour.

A page holds at most limit rows (default 100, at most 500) with next_cursor, null at the end. History is kept for the plan's event retention. Raw reports reach this view within seconds, hourly summaries within about 5 minutes. A screen of another project is not_found. Screen.health, the latest report, is unchanged.


Operation: getScreenHealthHistory.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| format | query | optional | json (default) returns the report as JSON. csv, xlsx and pdf return the same rows as a file attachment (every page of a paged report, up to 50000 rows). |
| from | query | optional | Inclusive lower bound on the report time. Defaults to 24 hours before to. |
| to | query | optional | Exclusive upper bound on the report time. Defaults to now. |
| resolution | query | optional | raw returns each report; hour returns one summary per UTC hour. |
| limit | query | optional | Page size. |
| cursor | query | optional | next_cursor of the previous page, with the same filters. Opaque. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/health/history'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | A page of reports or hourly summaries. | ScreenHealthHistory (application/json); headers: Cache-Control |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Get Screen Uptime**

Daily availability of one screen over inclusive UTC days (day_to defaults to today and day_from to 29 days before it, at most 366 days). Each row holds the online and offline seconds recorded that day, availability (online over recorded time, null when nothing was recorded) and coverage (the share of the day with a recorded state).

Time with no recorded state is never counted as downtime: it lowers coverage only. The screen's current open interval is included up to now and marks its days live. Days with nothing recorded have no row. Offline is recorded after the screen has been unreachable for 60 seconds. A screen of another project is not_found.


Operation: getScreenUptime.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| format | query | optional | json (default) returns the report as JSON. csv, xlsx and pdf return the same rows as a file attachment (every page of a paged report, up to 50000 rows). |
| day_from | query | optional | First UTC day, inclusive. Defaults to 29 days before day_to. With day_to spans at most 366 days. |
| day_to | query | optional | Last UTC day, inclusive. Defaults to today (UTC). |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/uptime'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Daily uptime rows, oldest first. | ScreenUptime (application/json); headers: Cache-Control |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

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

**Get Screen Presence**

The screen's online and offline transitions, newest first, from its screen.online and screen.offline events whatever the log lane stores. since (inclusive) and until (exclusive) bound the event time; until defaults to now and since to 30 days before it, at most 366 days. Transitions are kept for the plan's event retention.

A page holds at most limit transitions (default 100, at most 500) with next_cursor, null at the end; pass it back as before. An offline transition the server noticed only when the screen reconnected carries detected_on_reconnect, and its at is when the presence lease ended. Offline is recorded after the screen has been unreachable for 60 seconds.

A screen of another project is not_found.


Operation: getScreenPresence.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| since | query | optional | Inclusive lower bound on the transition event time. Defaults to 30 days before until. |
| until | query | optional | Exclusive upper bound on the transition event time. Defaults to now. until - since is at most 366 days. |
| limit | query | optional | Page size. A value outside 1-500 is refused with invalid_request naming limit. |
| before | query | optional | next_cursor of the previous page, with the same since and until. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/screens/RESOURCE_ID/presence'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Presence transitions, newest first. | ScreenPresenceHistory (application/json); headers: Cache-Control |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 404 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/health/trend

**Get Health Trend**

The project's fleet health by UTC hour: how many screens reported, and how many of them were hot (at or above 80 °C), had a disconnected display, or were crashing (3 or more crashes in 24 hours). from (inclusive) and to (exclusive) default to the last 24 hours, at most 93 days; a page holds at most limit rows (default 100, at most 500) with next_cursor, null at the end.

Hours follow reports by up to about 5 minutes. Refused to a caller limited to screens (outside_screen_scope); GET /api/screens/{id}/health/history is the per-screen form.


Operation: getHealthTrend.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| format | query | optional | json (default) returns the report as JSON. csv, xlsx and pdf return the same rows as a file attachment (every page of a paged report, up to 50000 rows). |
| from | query | optional | Inclusive lower bound on the report time. Defaults to 24 hours before to. |
| to | query | optional | Exclusive upper bound on the report time. Defaults to now. |
| limit | query | optional | Page size. |
| cursor | query | optional | next_cursor of the previous page, with the same filters. Opaque. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/health/trend'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | A page of hourly fleet health. | HealthTrend (application/json); headers: Cache-Control |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/uptime/daily

**Get Project Uptime**

Daily availability of the project's screens over inclusive UTC days (defaults and range as GET /api/screens/{id}/uptime). Each row sums the online and offline seconds of the screens with a recorded state that day; coverage is the share of those screens' day with a recorded state.

Intervals count when they end, so today's row covers intervals that ended so far; the live online count is in the dashboard summary. Refused to a caller limited to screens (outside_screen_scope); GET /api/screens/{id}/uptime is the per-screen form.


Operation: getProjectUptime.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| format | query | optional | json (default) returns the report as JSON. csv, xlsx and pdf return the same rows as a file attachment (every page of a paged report, up to 50000 rows). |
| day_from | query | optional | First UTC day, inclusive. Defaults to 29 days before day_to. With day_to spans at most 366 days. |
| day_to | query | optional | Last UTC day, inclusive. Defaults to today (UTC). |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://screenrig.ai/api/uptime/daily'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Daily uptime rows, oldest first. | ProjectUptime (application/json); headers: Cache-Control |
| 400 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 401 | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |
| 403 | outside_screen_scope - the caller is limited to screens and the screen, screen folder, selector or audience the request names or reaches is outside them (docs/screen-scope.md). An id outside the scope and an id that does not exist get the same answer; errors[0].screen_scope carries the caller's scope, and the write is refused whole, never narrowed. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/fleet-links

**List Fleet Links**

Lists the project's fleet links, oldest first (at most 50). Below effective Manage each url is empty and url_redacted is true, since the URL creates screens.


Operation: listFleetLinks.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/fleet-links'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The project's fleet links. | FleetLinkList (application/json); headers: Cache-Control |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/fleet-links

**Create Fleet Link**

Creates a fleet link: one Player URL (https://play.<domain>/player/#fleet=<secret>) that turns every device opening it into its own screen in this project, without a pairing code. Put it in a device manager, such as a Google Admin console kiosk app.

Each new screen gets the link's tags, fields and playlist, and a label from the device (its administrator asset ID, then location), else the next default label. A Player that already holds a screen never makes another: its key stays the screen's identity across reloads and relaunches.

After a powerwash or reinstall, a device that reports the serial or DUID of a screen a fleet link of this project created recovers that screen (screen.recovered) instead of creating a new one, once the old key has been silent for ten minutes.

The secret travels in the URL fragment, which browsers never send to a server. max_screens caps the screens the link creates and expires_at ends the link at that time, as disabling it does. An organization has at most 50 fleet links across its projects (fleet_link_limit_reached). Appends fleet_link.created.

An exact retry with the same Idempotency-Key returns this same answer for twenty-four hours.


Operation: createFleetLink.

- Authentication: projectBearer OR dashboardSession
- Request body: FleetLinkWrite

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| label | required | string | minLength: 1 maxLength: 80 |
| enabled | optional | boolean | Defaults to true. |
| tags | optional | ScreenTags | Fleet selector tags, 0 to 16 unique exact tags, each matching the media tag grammar. On ScreenPatch the array replaces the whole set and an empty array clears it; a duplicate or malformed tag is invalid_request. Changing tags bumps the screen revision (If-Match guards it) and appends screen.updated with details.tags; it never changes the manifest revision. Tags are never on the runtime manifest and never authorization. |
| fields | optional | object | Fields each screen the link creates starts with; card placements read them through {screen.field.NAME}. |
| playlist_id | optional | string |  |
| max_screens | optional | integer | minimum: 1 maximum: 10000 |
| expires_at | optional | string | When the link expires (RFC 3339); a link past it creates and recovers no screens. Absent means it never expires. A time already past is accepted and expires the link at once. format: "date-time" |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/fleet-links'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 201 | Created, with its url. | FleetLink (application/json); headers: Cache-Control |
| 400 | invalid_request, including an unknown playlist_id or a malformed tag or field. | Problem (application/problem+json) |
| 409 | fleet_link_limit_reached or idempotency_mismatch. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### GET /api/fleet-links/{id}

**Get Fleet Link**

One fleet link. Below effective Manage url is empty and url_redacted is true.


Operation: getFleetLink.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |

#### Request template

~~~sh
curl --request GET --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/fleet-links/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | One fleet link. | FleetLink (application/json); headers: ETag, Cache-Control |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### PATCH /api/fleet-links/{id}

**Update Fleet Link**

Changes any of label, enabled, tags, fields, playlist_id, max_screens and expires_at; null clears fields, playlist_id, max_screens or expires_at. A disabled or expired link creates and recovers no screens; a device opening it shows its pairing code instead. Changes apply to screens the link creates from now on, never to screens it already created.

Appends fleet_link.updated. If-Match guards the revision.


Operation: updateFleetLink.

- Authentication: projectBearer OR dashboardSession
- Request body: FleetLinkPatch

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request body fields

| Field | Required? | Type | Description |
| --- | --- | --- | --- |
| label | optional | string | minLength: 1 maxLength: 80 |
| enabled | optional | boolean |  |
| tags | optional | ScreenTags | Fleet selector tags, 0 to 16 unique exact tags, each matching the media tag grammar. On ScreenPatch the array replaces the whole set and an empty array clears it; a duplicate or malformed tag is invalid_request. Changing tags bumps the screen revision (If-Match guards it) and appends screen.updated with details.tags; it never changes the manifest revision. Tags are never on the runtime manifest and never authorization. |
| fields | optional | object | replaces the whole preset; {} or null clears it. |
| playlist_id | optional | string | null clears the playlist. |
| max_screens | optional | integer | null removes the cap. minimum: 1 maximum: 10000 |
| expires_at | optional | string | null removes the expiry. format: "date-time" |

#### Request template

~~~sh
curl --request PATCH --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt --header 'Content-Type: application/json' --data '@request.json' 'https://api.screenrig.ai/api/fleet-links/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | Updated fleet link. | FleetLink (application/json); headers: ETag |
| 400 | invalid_request. | Problem (application/problem+json) |
| 412 | revision_conflict — If-Match is stale; the error's current_revision carries the current revision. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### DELETE /api/fleet-links/{id}

**Delete Fleet Link**

Deletes the fleet link. Its URL stops working at once; screens it created stay, with their playlists, tags and fields. Appends fleet_link.deleted. If-Match guards the revision.


Operation: deleteFleetLink.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request DELETE --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/fleet-links/RESOURCE_ID'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 204 | Deleted. | no body |
| 412 | revision_conflict. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |

### POST /api/fleet-links/{id}/rotate

**Rotate Fleet Link**

Replaces the link's secret and returns the new url. The old URL stops working at once; screens already created keep playing, since a paired Player never presents the link again. Appends fleet_link.updated with details.rotated true. If-Match guards the revision.


Operation: rotateFleetLink.

- Authentication: projectBearer OR dashboardSession
- Request body: none

#### Parameters

| Name | Send in | Required? | Description |
| --- | --- | --- | --- |
| ScreenRig-Project | header | optional | Deprecated for one release. Must be consistent with the JWT projects grants; adds Deprecation and Warning headers. No token exchange. |
| id | path | required |  |
| If-Match | header | optional | Optional positive resource revision. Omit to write the current resource; a supplied stale revision is revision_conflict. |
| Idempotency-Key | header | optional | Omit for a fresh operation. Supply and reuse a key to deduplicate retries; the CLI manages keys automatically. |

#### Request template

~~~sh
curl --request POST --header 'Authorization: Bearer [SCREENRIG_TOKEN]' --cookie cookies.txt --cookie-jar cookies.txt 'https://api.screenrig.ai/api/fleet-links/RESOURCE_ID/rotate'
~~~

#### Responses

| Status | Meaning | Body and headers |
| --- | --- | --- |
| 200 | The fleet link with its new url. | FleetLink (application/json); headers: ETag, Cache-Control |
| 412 | revision_conflict. | Problem (application/problem+json) |
| default | Stable RFC 9457 problem response. A 421 server_draining carries Retry-After of 1 second, a 500 not_ready or dependency_unavailable carries 5, and a 500 manifest_degraded carries 60, unless the handler set a more specific value. | Problem (application/problem+json) |


## Sitemap

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