# Screens (part 2 of 4)

> For AI agents: the full docs index is at https://brixsignage.com/llms.txt, and every page has a markdown twin (add .md to the path).

Screens is split into 4 pages: [part 1](https://brixsignage.com/developers/api/screens.md) (`/v1/screens` to `/v1/screens/{id}`), [part 2](https://brixsignage.com/developers/api/screens-2.md) (`/v1/screens/{id}` to `/v1/screens/{id}/mirror`), [part 3](https://brixsignage.com/developers/api/screens-3.md) (`/v1/screens/{id}/outages` to `/v1/screens/{id}/unpower-and-delete`), [part 4](https://brixsignage.com/developers/api/screens-4.md) (`/v1/screens/{id}/up-next` to `/v1/screens/touch-content`).

Base URL `https://api.brixsignage.com`. Send `Authorization: Bearer $BRIX_API_KEY` unless an operation says Auth: none or Auth: no API key. The Permission line names what the key must hold. See [Authentication and scopes](https://brixsignage.com/developers/authentication.md), [Errors and rate limits](https://brixsignage.com/developers/errors-and-rate-limits.md) and [Pagination](https://brixsignage.com/developers/pagination.md).

- [DELETE /v1/screens/{id}](#delete-v1-screens-id)
- [POST /v1/screens/{id}/assign](#post-v1-screens-id-assign)
- [POST /v1/screens/{id}/claim-replacement](#post-v1-screens-id-claim-replacement)
- [POST /v1/screens/{id}/commands](#post-v1-screens-id-commands)
- [GET /v1/screens/{id}/commands/{commandId}](#get-v1-screens-id-commands-commandid)
- [GET /v1/screens/{id}/diagnose](#get-v1-screens-id-diagnose)
- [GET /v1/screens/{id}/diagnostic-bundles](#get-v1-screens-id-diagnostic-bundles)
- [GET /v1/screens/{id}/diagnostic-bundles/{bundleId}](#get-v1-screens-id-diagnostic-bundles-bundleid)
- [GET /v1/screens/{id}/diagnostics](#get-v1-screens-id-diagnostics)
- [GET /v1/screens/{id}/display-history](#get-v1-screens-id-display-history)
- [GET /v1/screens/{id}/frames](#get-v1-screens-id-frames)
- [POST /v1/screens/{id}/kiosk-recovery/reveal](#post-v1-screens-id-kiosk-recovery-reveal)
- [POST /v1/screens/{id}/kiosk-recovery/rotate](#post-v1-screens-id-kiosk-recovery-rotate)
- [POST /v1/screens/{id}/lan-secret/rotate](#post-v1-screens-id-lan-secret-rotate)
- [GET /v1/screens/{id}/live-thumbnail](#get-v1-screens-id-live-thumbnail)
- [GET /v1/screens/{id}/logs](#get-v1-screens-id-logs)
- [POST /v1/screens/{id}/mirror](#post-v1-screens-id-mirror)

Every 4xx and 5xx response below returns this body:

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## DELETE /v1/screens/{id}

Delete a screen. It moves to the 30-day recovery window, and its paired device is unpaired so the player shows its pairing screen. An optional `reason` field is recorded in the activity log.

Soft-deletes the screen to the recycle bin (restorable for 30 days) and releases its device, which returns to the pairing screen.

Auth: Bearer token. Permission: `screen.delete`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl -X DELETE "https://api.brixsignage.com/v1/screens/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.deleted` | true |  |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No such screen in this workspace.

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/assign

Set what a screen plays

Assign content to a screen using `contentKind` and `contentId`. Pass `null` to clear the current assignment. The content is validated to confirm it exists, can be used at the screen's organization node, and has no unfilled layout zones, before the screen is updated to play it.

Assigns content to one screen (null clears it). The screen is told to reload immediately.

Auth: Bearer token. Permission: `screen.cast`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

Request body: `application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `contentKind` | "playlist" \| "schedule" \| "layout" \| "creative" \| "app" \| "media" \| "signage" \| null | yes | What to assign; null clears the assignment. |
| `contentId` | string | no | Required unless `contentKind` is null. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/assign" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contentKind":"playlist","contentId":"pl_9a8b7c6d5e4f3a2b"}'
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.contentKind` | "playlist" \| "schedule" \| "layout" \| "creative" \| "app" \| "media" \| "signage" \| null |  |
| `data.contentId` | string \| null |  |

```json
{
  "data": {
    "id": "scr_1a2b3c4d5e6f7a8b",
    "contentKind": "playlist",
    "contentId": "pl_9a8b7c6d5e4f3a2b"
  }
}
```

Response 401: Missing, expired or revoked bearer token.

Response 403: `not_shared`: the content is not shared to this screen's location.

Response 404: No such screen or content in this workspace.

Response 422: Invalid kind, a layout with unbound zones, or content that cannot play (empty playlist, unprocessed media).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/claim-replacement

Replace a screen's device

Bind a new device, identified by the 6-digit pairing code it is displaying, to an existing screen. The screen keeps its identity, assigned content, and history. This endpoint is rate-limited because the pairing code is a small, guessable value.

Auth: Bearer token. Permission: `screen.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

Request body: `application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | yes | The 6-digit code the new device shows. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/claim-replacement" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.paired` | true |  |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No screen with this id, or no device shows that code.

Response 409: `ambiguous_code` (two devices show it) or `already_claimed`.

Response 410: The code has expired.

Response 422: `code` is missing.

Response 429: Too many wrong codes; wait and try again.

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/commands

Send a command to a screen

Queue a command for a screen's device, such as reboot, turning the display on or off, taking a screenshot, or clearing the cache. The device runs the command the next time it checks in.

Queues a device command (reboot, refresh, screenshot, volume, …). Poll GET /v1/screens/{id}/commands/{commandId} for the device's answer.

**Notes.**
- A `screenshot` that coalesces onto an older pending one answers 200 (not 201) with `coalesced: true`.

Auth: Bearer token. Permission: `screen.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

Request body: `application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | string | yes |  |
| `payload` | object | no | Per-kind payload: `set-volume` {level 0-100}, `set-brightness` {level}, `set-mute` {muted}, `set-input` {input}, … Most kinds take none. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/commands" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string | Command id. |
| `data.kind` | string |  |
| `data.status` | "pending" |  |
| `data.coalesced` | boolean | True when a stale pending screenshot command was reused instead. |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No such screen in this workspace.

Response 409: `wifi_already_saved`: an `add-wifi` or `set-fallback-wifi` names a network the screen already has saved. A wrong password for a saved network takes the screen offline, so only new networks are sent.

Response 422: Unknown kind or invalid payload.

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## GET /v1/screens/{id}/commands/{commandId}

Get a command's status

Get the outcome of one previously queued device command, including its status (acknowledged, failed, or pending) and any note reported by the device.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |
| `commandId` | path | string | yes | Command id from POST /v1/screens/{id}/commands. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/commands/{commandId}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.kind` | string | The command kind (see POST /v1/screens/{id}/commands). |
| `data.status` | "pending" \| "delivered" \| "acked" \| "failed" \| "unconfirmed" \| "held" |  |
| `data.result` | string \| null |  |
| `data.issuedAt` | string | ISO-8601 timestamp (UTC). |
| `data.deliveredAt` | string \| null |  |
| `data.ackedAt` | string \| null |  |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

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

Diagnose a screen issue

Get a likely-cause troubleshooting verdict for a screen, based on signals such as online or offline status, what is actually rendering, resource pressure, and early hardware warnings. The response also includes a guided checklist for checking the TV, input, and cable, which the screen itself cannot detect.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/diagnose" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | ScreenDiagnosis | Why a screen is (or is not) showing what it should. |
| `data.layer` | "offline" \| "deactivated" \| "unassigned" \| "stick" \| "resource" \| "display" \| "downstream" \| "healthy" | The first layer that explains the problem, checked from the device outward. |
| `data.severity` | "none" \| "info" \| "warning" \| "critical" |  |
| `data.likelyCause` | string | One sentence an operator can act on. |
| `data.detail` | string \| null |  |
| `data.fault` | "platform" \| "user" \| "environment" \| null | Whose problem it is; null when healthy. |
| `data.guided` | boolean | True when `checklist` is a guided fix. |
| `data.checklist` | array of object |  |
| `data.checklist[].code` | string |  |
| `data.checklist[].label` | string |  |
| `data.checklist[].detail` | string |  |
| `data.remedies` | array of object | One-click fixes, when one applies. |
| `data.remedies[].kind` | "reboot" \| "wake-screen" \| "restart-app" | The command to send with POST /v1/screens/{id}/commands. |
| `data.remedies[].label` | string |  |
| `data.remedies[].cost` | string | What the remedy interrupts, in words. |
| `data.remedies[].confirm` | boolean |  |
| `data.prominent` | boolean | True when the console shows this as a banner. |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## GET /v1/screens/{id}/diagnostic-bundles

List a screen's diagnostic bundles

List the detailed diagnostic bundles a screen has uploaded, such as system logs, thread dumps, and exit reasons, newest first. This returns only the index; download an individual bundle separately.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/diagnostic-bundles" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].id` | string |  |
| `data[].reason` | string |  |
| `data[].sections` | array of string |  |
| `data[].bytes` | integer |  |
| `data[].downloadable` | boolean | False when the bundle was recorded but not stored. |
| `data[].viewable` | boolean | True when `?view=1` on the download route answers for this workspace (the in-CMS reader is on). |
| `data[].playerVersion` | string \| null |  |
| `data[].platform` | string \| null |  |
| `data[].clockSkewMs` | number \| null |  |
| `data[].createdAt` | string | ISO-8601 timestamp (UTC). |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## GET /v1/screens/{id}/diagnostic-bundles/{bundleId}

Download a diagnostic bundle

Download one detailed diagnostic bundle as JSON, exactly as it was uploaded by the device.

**Notes.**
- The body is the stored bundle (v2: `summary`, `gaps` = missing parts with the reason, `parts`), served as an attachment. Bundles are kept 30 days.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |
| `bundleId` | path | string | yes | Bundle id. |
| `view` | query | string | no | `1` returns the bundle inside the `{ data }` envelope instead of as a file (only where `viewable` is true). |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/diagnostic-bundles/{bundleId}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No such bundle, or it was not stored.

Response 5XX: Server error. The body carries a `requestId` to quote to support.

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

Get consolidated screen diagnostics

Get a consolidated diagnostic report for a screen: the likely-cause verdict from GET /v1/screens/:id/diagnose plus the underlying signals, including open alerts, recent health trends, crashes and self-heals, a recent log tail, and the latest reported state. Pass `format=text` for a plain-text report, or omit it for structured JSON.

**Notes.**
- `?format=text` returns the same report as plain text (text/plain) instead of JSON.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/diagnostics" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.screen` | object |  |
| `data.screen.id` | string |  |
| `data.screen.name` | string |  |
| `data.screen.status` | "online" \| "offline" \| "pairing" |  |
| `data.screen.lastSeenAt` | string \| null |  |
| `data.diagnosis` | ScreenDiagnosis | Why a screen is (or is not) showing what it should. |
| `data.diagnosis.layer` | "offline" \| "deactivated" \| "unassigned" \| "stick" \| "resource" \| "display" \| "downstream" \| "healthy" | The first layer that explains the problem, checked from the device outward. |
| `data.diagnosis.severity` | "none" \| "info" \| "warning" \| "critical" |  |
| `data.diagnosis.likelyCause` | string | One sentence an operator can act on. |
| `data.diagnosis.detail` | string \| null |  |
| `data.diagnosis.fault` | "platform" \| "user" \| "environment" \| null | Whose problem it is; null when healthy. |
| `data.diagnosis.guided` | boolean | True when `checklist` is a guided fix. |
| `data.diagnosis.checklist` | array of object |  |
| `data.diagnosis.checklist[].code` | string |  |
| `data.diagnosis.checklist[].label` | string |  |
| `data.diagnosis.checklist[].detail` | string |  |
| `data.diagnosis.remedies` | array of object | One-click fixes, when one applies. |
| `data.diagnosis.remedies[].kind` | "reboot" \| "wake-screen" \| "restart-app" | The command to send with POST /v1/screens/{id}/commands. |
| `data.diagnosis.remedies[].label` | string |  |
| `data.diagnosis.remedies[].cost` | string | What the remedy interrupts, in words. |
| `data.diagnosis.remedies[].confirm` | boolean |  |
| `data.diagnosis.prominent` | boolean | True when the console shows this as a banner. |
| `data.alerts` | array of object | Open alerts on this screen. |
| `data.alerts[].code` | string |  |
| `data.alerts[].severity` | string |  |
| `data.alerts[].message` | string |  |
| `data.alerts[].openedAt` | string | ISO-8601 timestamp (UTC). |
| `data.vitals` | array of object | Recent device vitals, newest first. |
| `data.vitals[].id` | string |  |
| `data.vitals[].spaceId` | string |  |
| `data.vitals[].screenId` | string |  |
| `data.vitals[].storageFreeMb` | number \| null |  |
| `data.vitals[].storageTotalMb` | number \| null |  |
| `data.vitals[].memoryFreeMb` | number \| null |  |
| `data.vitals[].memoryTotalMb` | number \| null |  |
| `data.vitals[].heapUsedMb` | number \| null |  |
| `data.vitals[].heapLimitMb` | number \| null |  |
| `data.vitals[].networkLossRatio` | number \| null |  |
| `data.vitals[].networkRttMs` | number \| null |  |
| `data.vitals[].thermalStatus` | string \| null |  |
| `data.vitals[].reloadCount` | integer \| null |  |
| `data.vitals[].rebootCount` | integer \| null |  |
| `data.vitals[].uptimeSec` | number \| null |  |
| `data.vitals[].fps` | number \| null |  |
| `data.vitals[].videoDropPct` | number \| null |  |
| `data.vitals[].webViewKillCount` | integer \| null |  |
| `data.vitals[].renderFreezeCount` | integer \| null |  |
| `data.vitals[].maxRenderFreezeMs` | number \| null |  |
| `data.vitals[].childFreezeCount` | integer \| null |  |
| `data.vitals[].cpuTempC` | number \| null |  |
| `data.vitals[].gpuTempC` | number \| null |  |
| `data.vitals[].maxTempC` | number \| null | Hottest reading since the previous heartbeat (°C). |
| `data.vitals[].starvedAdvanceCount` | integer \| null |  |
| `data.vitals[].longTasks` | integer \| null |  |
| `data.vitals[].videoSeamWorstMs` | number \| null |  |
| `data.vitals[].videoHoleMaxMs` | number \| null |  |
| `data.vitals[].videoStartMaxMs` | number \| null | Slowest video start since the previous heartbeat: play() to the first moving frame (ms). |
| `data.vitals[].videoCallbackStallMs` | number \| null | Longest late frame callback while frames were still shown (ms) — a busy player, not a blank screen. |
| `data.vitals[].videoMidStallMaxMs` | number \| null | Longest run since the previous heartbeat in which a playing clip's picture stood still (≥300 ms, ms; migration 0306). |
| `data.vitals[].decodeErrors` | integer \| null |  |
| `data.vitals[].zonesSuppressed` | integer \| null |  |
| `data.vitals[].videoBudgetHits` | integer \| null |  |
| `data.vitals[].hdmiConnected` | integer \| null | 1 = an HDMI sink answers, 0 = none, null = unknown. |
| `data.vitals[].frameGapCount` | integer \| null |  |
| `data.vitals[].frameGapMaxMs` | number \| null |  |
| `data.vitals[].anrCount` | integer \| null |  |
| `data.vitals[].memoryDegradeStep` | integer \| null |  |
| `data.vitals[].decoderInitFailures` | integer \| null |  |
| `data.vitals[].cacheWipeCount` | integer \| null |  |
| `data.vitals[].liveVideoEls` | integer \| null |  |
| `data.vitals[].liveIframes` | integer \| null |  |
| `data.vitals[].maxLiveVideoEls` | integer \| null |  |
| `data.vitals[].liveDecoders` | integer \| null | Videos holding a hardware decoder at heartbeat time (migration 0306). |
| `data.vitals[].decoderBudget` | integer \| null | Video decoders the player allowed itself at heartbeat time (RAM band, memory ladder, heat, override). Null = no limit or an older player (migration 0325). |
| `data.vitals[].decoderBudgetAgeSec` | integer \| null | Seconds that decoder budget had been in force (migration 0325). |
| `data.vitals[].decoderOverrideFellBackFrom` | integer \| null | The videoDecoderMax override the screen gave up because its extra decoder would not start (migration 0325). |
| `data.vitals[].launchReason` | string \| null |  |
| `data.vitals[].launchAt` | number \| null | When this player run started (epoch ms). |
| `data.vitals[].coreVersion` | string \| null | The player core version that sent this sample (migration 0305). |
| `data.vitals[].occurredAt` | string | ISO-8601 timestamp (UTC). |
| `data.crashes` | array of object |  |
| `data.crashes[].reason` | string |  |
| `data.crashes[].reportedAt` | string | ISO-8601 timestamp (UTC). |
| `data.logs` | array of object |  |
| `data.logs[].entries` | any | The uploaded log lines (decoded JSON). |
| `data.logs[].reason` | string |  |
| `data.logs[].errorCount` | integer |  |
| `data.logs[].warnCount` | integer |  |
| `data.logs[].createdAt` | string | ISO-8601 timestamp (UTC). |
| `data.state` | object \| null | The last heartbeat snapshot; null before the first heartbeat. |
| `data.generatedAt` | string | ISO-8601 timestamp (UTC). |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## GET /v1/screens/{id}/display-history

Get a screen's display history

Get the history of what the physical display itself did, such as being switched off, switched to another HDMI input, losing its HDMI connection, or coming back, separate from whether the player software was online. Entries are newest first, each showing how long the previous state lasted, along with the current state. This data is only available for displays that support CEC, and history is limited to the last 14 days.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |
| `days` | query | string | no | Window in days; default and maximum: the telemetry retention. |
| `limit` | query | string | no | Most changes to list (default 50, max 500). |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/display-history" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.screenId` | string |  |
| `data.windowDays` | integer |  |
| `data.retentionDays` | integer |  |
| `data.current` | object \| null | What the panel is doing now; null when the device cannot read the panel. |
| `data.changes` | array of object |  |
| `data.changes[].id` | string |  |
| `data.changes[].at` | string | ISO-8601 timestamp (UTC). |
| `data.changes[].from` | "standby" \| "wrong-input" \| "disconnected" \| "input-unconfirmed" \| "lit" |  |
| `data.changes[].to` | "standby" \| "wrong-input" \| "disconnected" \| "input-unconfirmed" \| "lit" |  |
| `data.changes[].forSec` | number \| null |  |
| `data.changes[].basis` | "probe" \| "seen-switch" \| "stored-guess" | What the TV input reading stood on: a probe the player ran, an input change it saw, or its last stored value. |
| `data.changes[].basisLabel` | string | `basis` in plain words. |
| `data.truncated` | boolean |  |
| `data.dailySummaries` | array of string | The device's daily TV input summaries in the window, newest first, one line each. |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

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

Get a screen's frame history

Get a log of visual fingerprints captured from a screen over a time range, using the `since` and `until` query parameters. This log is a visual audit trail and is kept even after the screen is deleted.

**Notes.**
- The newest 500 frames in the window.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |
| `since` | query | string | no | ISO timestamp; only frames at or after it. |
| `until` | query | string | no | ISO timestamp; only frames at or before it. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/frames" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].hash` | string | Perceptual hash of the frame on the glass. |
| `data[].contentKind` | string \| null |  |
| `data[].contentId` | string \| null |  |
| `data[].capturedAt` | string | ISO-8601 timestamp (UTC). |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/kiosk-recovery/reveal

Reveal a screen's recovery code

Reveal the offline recovery code for a screen with Screen Lock enabled. This action is recorded in the activity log.

Auth: Bearer token. Permission: `screen.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/kiosk-recovery/reveal" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.code` | string | The Screen Lock recovery code. Each reveal is recorded in the audit log. |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No screen with this id, or it has no recovery code.

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/kiosk-recovery/rotate

Rotate a screen's recovery code

Generate a new offline recovery code for a screen with Screen Lock enabled. The previous code stops working immediately. This action is recorded in the activity log.

Auth: Bearer token. Permission: `screen.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/kiosk-recovery/rotate" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.code` | string | The new Screen Lock recovery code. |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/lan-secret/rotate

Rotate a screen's local network key

Replace the key used to authenticate local-network requests to this screen. The new key is not returned in the response; only the device receives it. Any third-party integration that signs its own requests to the screen must be updated with the new key.

**Notes.**
- The key itself is never returned; the device collects it.

Auth: Bearer token. Permission: `screen.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/lan-secret/rotate" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.rotatedAt` | string | ISO-8601 timestamp (UTC). |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No screen with this id, or Local trigger is off for it.

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## GET /v1/screens/{id}/live-thumbnail

Get a screen's live thumbnail

Get the most recently captured still image for a screen, returned as image bytes. This accepts standard API authentication, including an API key, or a short-lived `asset` query parameter token so the image can be loaded directly in an image tag. A screen outside your access scope returns a not-found error rather than a permission error.

**Notes.**
- The response header `x-captured-at` carries the capture time.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |
| `at` | query | string | no | `capturedAt` of a specific capture; default the newest. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/live-thumbnail" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 404: No screen with this id, or it has no capture yet (`no_screenshot`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

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

Get a screen's activity log

Get a combined, newest-first feed of a screen's recent activity from the last 24 hours, including telemetry events, crash reports, command acknowledgements, and screenshot uploads.

Auth: Bearer token. Permission: `screen.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl "https://api.brixsignage.com/v1/screens/{id}/logs" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.lines` | array of object | Newest first, at most 200. |
| `data.lines[].at` | string | ISO-8601 timestamp (UTC). |
| `data.lines[].level` | "error" \| "info" \| "warn" |  |
| `data.lines[].text` | string |  |
| `data.windowHours` | integer |  |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.

## POST /v1/screens/{id}/mirror

Start a screen mirror session

Start a live mirroring session for a screen. The response includes a signaling URL that a viewer connects to over WebRTC; the screen's device connects when it receives the corresponding command. End the session with POST /v1/mirror/:sessionId/end.

Auth: Bearer token. Permission: `screen.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Screen id. |

```bash
curl -X POST "https://api.brixsignage.com/v1/screens/{id}/mirror" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.sessionId` | string |  |
| `data.signalingUrl` | string |  |
| `data.signalingToken` | string | Viewer token for `signalingUrl`; valid for 5 minutes, for this session only. |
| `data.turnHint` | string |  |

Response 401: Missing, expired or revoked bearer token.

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

Response 5XX: Server error. The body carries a `requestId` to quote to support.
