Media API (part 1 of 3)
Media endpoints in the Brix REST API, part 1 of 3: 4 operations from /v1/media, each with auth, permission and curl.
View as Markdown · For AI agents: the full docs index is at /llms.txt
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, Errors and rate limits and Pagination.
Media is split into 3 pages: part 1 (/v1/media to /v1/media/{id}), part 2 (/v1/media/{id} to /v1/media/{id}/web-secrets), part 3 (/v1/media/backfill-avif to /v1/media/upload/package).
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. |
GET /v1/media
Return the media library: images, videos, PDFs, presentations, fonts, web links, and app-backed assets. Use limit and cursor to page through results, count=1 to include a total count, search to search by name or tag, and the comma-separated kind, state, and folderId parameters to filter. Paging is optional but recommended, since a library can hold thousands of items.
Without limit, every visible asset comes back unordered and without nextCursor. With limit, rows are newest first.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
search | query | string | no | Case-insensitive match on name or tags. |
kind | query | string | no | Comma-separated kinds, e.g. image,video. |
state | query | string | no | Comma-separated processing states. |
folderId | query | string | no | Comma-separated folder ids. |
usableAt | query | string | no | Location id: only assets that may play there (own, root library, or shared in). |
limit | query | integer | no | Page size (max 500). Omit to get every row. |
cursor | query | string | no | The nextCursor of the previous page. |
count | query | "1" | no | With limit: also return total. |
curl "https://api.brixsignage.com/v1/media" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | array of MediaAsset | |
data[].id | string | Media asset id. |
data[].spaceId | string | Workspace id. |
data[].name | string | |
data[].kind | string | Media kind: image, video, audio, pdf, powerpoint, web, dashboard, weather, rss, clock, qr, menu, directory, donor-wall, hall-of-fame, birthday-board, recognition, wayfinding, check-in, emergency, touch-kiosk, package, font. Free text in storage, so a very old row may carry another value. |
data[].url | string | Where the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address. |
data[].bytes | integer | Stored size in bytes (0 for links and apps). |
data[].checksum | string | null | |
data[].active | boolean | False when archived. |
data[].folderId | string | null | |
data[].tags | any | Tags — a JSON array of strings for every row written by the API (decoded from storage; left as the raw text if the stored value is not valid JSON). |
data[].altText | string | null | |
data[].thumbnailUrl | string | null | |
data[].width | integer | null | |
data[].height | integer | null | |
data[].durationSec | number | null | Video/audio length in seconds. |
data[].pdfPageCount | integer | null | |
data[].loopDurationMs | integer | null | Animated image loop length. |
data[].fontFamily | string | null | |
data[].fontWeights | any | null | Font assets: the weights in the file (decoded JSON). |
data[].state | string | Processing state: ready, processing, ready_with_warnings, blocked, … |
data[].stateReason | string | null | |
data[].stateCode | string | null | |
data[].stateFault | "user" | "platform" | null | |
data[].stateProgress | integer | null | |
data[].codec | string | null | |
data[].playableRev | integer | Bumped whenever the playable bytes change. |
data[].loopState | "queued" | "ready" | "failed" | null | |
data[].loopCodecs | string | null | |
data[].loopRev | integer | null | |
data[].loopRotation | integer | null | |
data[].loopBytes | integer | null | |
data[].seqState | "queued" | "ready" | "failed" | null | Playlist-sequencer rendition state (video only; minted when the video plays in a run of consecutive videos). |
data[].seqCodecs | string | null | |
data[].seqRev | integer | null | |
data[].seqRotation | integer | null | |
data[].seqBytes | integer | null | |
data[].seqConform | integer | null | |
data[].masterKey | string | null | |
data[].masterBytes | integer | null | |
data[].masterProbe | string | null | |
data[].rendition4k | string | null | |
data[].frameLumaMean | number | null | |
data[].frameLumaVariance | number | null | |
data[].frameEdgeDensity | number | null | |
data[].startsAt | string | null | Plays only from this time. |
data[].expiresAt | string | null | Stops playing after this time. |
data[].autoArchiveOnExpiry | boolean | |
data[].qr | any | null | QR overlay settings (decoded JSON). |
data[].webConfig | any | null | Web link settings — refresh, zoom, header auth, … (decoded JSON). |
data[].replayState | "healthy" | "replay_pending" | "replay_failed" | null | |
data[].lastReplayAt | string | null | |
data[].lastReplaySuccessAt | string | null | |
data[].lastFailedStep | integer | null | |
data[].lastFailedReason | string | null | |
data[].autoCaption | boolean | |
data[].captionTrackKey | string | null | |
data[].captionState | "pending" | "ready" | "failed" | null | |
data[].showCaptions | boolean | |
data[].audioEnabled | boolean | |
data[].focalRegion | string | null | Smart-fit focal region, as stored JSON text. |
data[].fit | "contain" | "cover" | "fill" | "blur-fill" | How the asset fills a box. |
data[].autoSmartFit | boolean | |
data[].rotation | integer | Clockwise rotation in degrees. |
data[].originalFormat | string | null | |
data[].packageEntry | string | null | |
data[].packageFiles | integer | null | |
data[].packageBytes | integer | null | |
data[].importSourceId | string | null | |
data[].copySourceId | string | null | The partner library item this was copied from (Copy to client workspace), or null. |
data[].nodeId | string | null | Home location; null = workspace library root. |
data[].recalledAt | string | null | |
data[].recalledBy | string | null | |
data[].createdAt | string | ISO-8601 timestamp (UTC). |
data[].updatedAt | string | ISO-8601 timestamp (UTC). |
data[].deletedAt | string | null | |
data[].usageCount | integer | Playlists, schedules, layouts, creatives, boards and screens that use this asset. |
data[].uploadedByName | string | null | Who uploaded it, from the audit log. |
data[].uploadSource | "ui" | "api" | "mcp" | null | How it was uploaded. |
nextCursor | string | null | Present when ?limit was passed. Send it back as ?cursor= for the next page; null on the last page. |
total | integer | Total matching rows, when the route computes it. |
{
"data": [
{
"id": "med_0c1d2e3f4a5b6c7d",
"spaceId": "space_1a2b3c4d5e6f7a8b",
"name": "lunch-special.jpg",
"kind": "image",
"url": "/v1/media/med_0c1d2e3f4a5b6c7d/file",
"bytes": 482113,
"checksum": null,
"active": true,
"folderId": null,
"tags": [
"menu",
"lunch"
],
"altText": "Grilled chicken wrap with fries",
"thumbnailUrl": null,
"width": 1920,
"height": 1080,
"durationSec": null,
"pdfPageCount": null,
"loopDurationMs": null,
"fontFamily": null,
"fontWeights": null,
"state": "ready",
"stateReason": null,
"stateCode": null,
"stateFault": null,
"stateProgress": null,
"codec": null,
"playableRev": 0,
"loopState": null,
"loopCodecs": null,
"loopRev": null,
"loopRotation": null,
"loopBytes": null,
"seqState": null,
"seqCodecs": null,
"seqRev": null,
"seqRotation": null,
"seqBytes": null,
"seqConform": null,
"masterKey": null,
"masterBytes": null,
"masterProbe": null,
"rendition4k": null,
"frameLumaMean": null,
"frameLumaVariance": null,
"frameEdgeDensity": null,
"startsAt": null,
"expiresAt": null,
"autoArchiveOnExpiry": false,
"qr": null,
"webConfig": null,
"replayState": null,
"lastReplayAt": null,
"lastReplaySuccessAt": null,
"lastFailedStep": null,
"lastFailedReason": null,
"autoCaption": false,
"captionTrackKey": null,
"captionState": null,
"showCaptions": true,
"audioEnabled": false,
"focalRegion": null,
"fit": "contain",
"autoSmartFit": true,
"rotation": 0,
"originalFormat": null,
"packageEntry": null,
"packageFiles": null,
"packageBytes": null,
"importSourceId": null,
"nodeId": null,
"recalledAt": null,
"recalledBy": null,
"createdAt": "2026-09-20T10:15:00.000Z",
"updatedAt": "2026-09-20T10:15:00.000Z",
"deletedAt": null,
"usageCount": 2,
"uploadedByName": "Sam Rivera",
"uploadSource": "ui"
}
],
"nextCursor": null,
"total": 1
} 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/media
Create a media asset record for a file whose bytes are already stored elsewhere, by providing name, kind, and url. This creates only the metadata record; to upload new file bytes, use POST /v1/media/upload, PUT /v1/media/upload/:uploadId, or POST /v1/media/import-url instead. Notes. - The 201 body is the row as written, not re-read: columns the create does not set are absent rather than null. GET returns every column. - Use this for links (web pages, streams, dashboards). To add a FILE, use POST /v1/media/upload, the multipart upload, or POST /v1/media/import-url.
Request body: application/json
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | |
kind | string | yes | |
url | string | yes | An https URL, a /v1/ path, or (for a web link) the page address. Private and internal network addresses are refused. |
active | boolean | no | |
folderId | string | null | no | |
tags | array of string | no | |
altText | string | null | no | |
thumbnailUrl | string | null | no | |
width | integer | null | no | |
height | integer | null | no | |
durationSec | number | null | no | |
startsAt | string | null | no | |
expiresAt | string | null | no | |
autoArchiveOnExpiry | boolean | no | |
qr | object | null | no | |
webConfig | object | null | no | Web link settings (refresh, zoom, …). |
nodeId | string | null | no | Home location. Default: the caller's own location. |
audioEnabled | boolean | no | |
fit | "contain" | "cover" | "fill" | "blur-fill" | no |
curl -X POST "https://api.brixsignage.com/v1/media" \
-H "Authorization: Bearer $BRIX_API_KEY" \
-H "Content-Type: application/json" Response 201: Success.
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string | Media asset id. |
data.spaceId | string | Workspace id. |
data.name | string | |
data.kind | string | Media kind: image, video, audio, pdf, powerpoint, web, dashboard, weather, rss, clock, qr, menu, directory, donor-wall, hall-of-fame, birthday-board, recognition, wayfinding, check-in, emergency, touch-kiosk, package, font. Free text in storage, so a very old row may carry another value. |
data.url | string | Where the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address. |
data.bytes | integer | Stored size in bytes (0 for links and apps). |
data.active | boolean | False when archived. |
data.tags | any | Tags — a JSON array of strings for every row written by the API (decoded from storage; left as the raw text if the stored value is not valid JSON). |
data.state | string | Processing state: ready, processing, ready_with_warnings, blocked, … |
data.autoArchiveOnExpiry | boolean | |
data.nodeId | string | null | Home location; null = workspace library root. |
data.folderId | string | null | |
data.altText | string | null | |
data.thumbnailUrl | string | null | |
data.width | integer | null | |
data.height | integer | null | |
data.durationSec | number | null | Video/audio length in seconds. |
data.startsAt | string | null | Plays only from this time. |
data.expiresAt | string | null | Stops playing after this time. |
data.qr | any | null | QR overlay settings (decoded JSON). |
data.webConfig | any | null | Web link settings — refresh, zoom, header auth, … (decoded JSON). |
data.audioEnabled | boolean | |
data.fit | "contain" | "cover" | "fill" | "blur-fill" | How the asset fills a box. |
data.createdAt | string | ISO-8601 timestamp (UTC). |
data.updatedAt | string | ISO-8601 timestamp (UTC). |
data.deletedAt | null |
Response 400: fit is not a known value.
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 404: The folder or location does not exist.
Response 422: name, kind or url is missing, or kind is unknown.
Response 5XX: Server error. The body carries a requestId to quote to support.
GET /v1/media/{id}
Return one media asset with its full details, including kind, url, dimensions, duration, tags, folder, play window, and web configuration.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Media asset id. |
curl "https://api.brixsignage.com/v1/media/{id}" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | MediaAssetDetail | One media asset, as GET /v1/media/{id} returns it. |
data.id | string | Media asset id. |
data.spaceId | string | Workspace id. |
data.name | string | |
data.kind | string | Media kind: image, video, audio, pdf, powerpoint, web, dashboard, weather, rss, clock, qr, menu, directory, donor-wall, hall-of-fame, birthday-board, recognition, wayfinding, check-in, emergency, touch-kiosk, package, font. Free text in storage, so a very old row may carry another value. |
data.url | string | Where the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address. |
data.bytes | integer | Stored size in bytes (0 for links and apps). |
data.checksum | string | null | |
data.active | boolean | False when archived. |
data.folderId | string | null | |
data.tags | any | Tags — a JSON array of strings for every row written by the API (decoded from storage; left as the raw text if the stored value is not valid JSON). |
data.altText | string | null | |
data.thumbnailUrl | string | null | |
data.width | integer | null | |
data.height | integer | null | |
data.durationSec | number | null | Video/audio length in seconds. |
data.pdfPageCount | integer | null | |
data.loopDurationMs | integer | null | Animated image loop length. |
data.fontFamily | string | null | |
data.fontWeights | any | null | Font assets: the weights in the file (decoded JSON). |
data.state | string | Processing state: ready, processing, ready_with_warnings, blocked, … |
data.stateReason | string | null | |
data.stateCode | string | null | |
data.stateFault | "user" | "platform" | null | |
data.stateProgress | integer | null | |
data.codec | string | null | |
data.playableRev | integer | Bumped whenever the playable bytes change. |
data.loopState | "queued" | "ready" | "failed" | null | |
data.loopCodecs | string | null | |
data.loopRev | integer | null | |
data.loopRotation | integer | null | |
data.loopBytes | integer | null | |
data.seqState | "queued" | "ready" | "failed" | null | Playlist-sequencer rendition state (video only; minted when the video plays in a run of consecutive videos). |
data.seqCodecs | string | null | |
data.seqRev | integer | null | |
data.seqRotation | integer | null | |
data.seqBytes | integer | null | |
data.seqConform | integer | null | |
data.masterKey | string | null | |
data.masterBytes | integer | null | |
data.masterProbe | string | null | |
data.rendition4k | string | null | |
data.frameLumaMean | number | null | |
data.frameLumaVariance | number | null | |
data.frameEdgeDensity | number | null | |
data.startsAt | string | null | Plays only from this time. |
data.expiresAt | string | null | Stops playing after this time. |
data.autoArchiveOnExpiry | boolean | |
data.qr | any | null | QR overlay settings (decoded JSON). |
data.webConfig | any | null | Web link settings — refresh, zoom, header auth, … (decoded JSON). |
data.replayState | "healthy" | "replay_pending" | "replay_failed" | null | |
data.lastReplayAt | string | null | |
data.lastReplaySuccessAt | string | null | |
data.lastFailedStep | integer | null | |
data.lastFailedReason | string | null | |
data.autoCaption | boolean | |
data.captionTrackKey | string | null | |
data.captionState | "pending" | "ready" | "failed" | null | |
data.showCaptions | boolean | |
data.audioEnabled | boolean | |
data.focalRegion | string | null | Smart-fit focal region, as stored JSON text. |
data.fit | "contain" | "cover" | "fill" | "blur-fill" | How the asset fills a box. |
data.autoSmartFit | boolean | |
data.rotation | integer | Clockwise rotation in degrees. |
data.originalFormat | string | null | |
data.packageEntry | string | null | |
data.packageFiles | integer | null | |
data.packageBytes | integer | null | |
data.importSourceId | string | null | |
data.copySourceId | string | null | The partner library item this was copied from (Copy to client workspace), or null. |
data.nodeId | string | null | Home location; null = workspace library root. |
data.recalledAt | string | null | |
data.recalledBy | string | null | |
data.createdAt | string | ISO-8601 timestamp (UTC). |
data.updatedAt | string | ISO-8601 timestamp (UTC). |
data.deletedAt | string | null | |
data.usageCount | integer | Playlists, schedules, layouts, creatives, boards and screens that use this asset. |
data.uploadedByName | string | null | Who uploaded it, from the audit log. |
data.uploadSource | "ui" | "api" | "mcp" | null | How it was uploaded. |
data.format | string | null | File format read from the stored bytes (PNG, MP4 (hevc)), only when the name has no usable extension; otherwise null. |
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 asset in this workspace (or it is in the recycle bin).
Response 5XX: Server error. The body carries a requestId to quote to support.
PATCH /v1/media/{id}
Edit a media asset. Accepts name, altText, tags, folderId, node, play-window fields (startsAt, expiresAt, autoArchiveOnExpiry), audioEnabled, a QR code overlay, and web configuration. Changing the URL of a web-kind asset re-checks it to make sure it is safe to fetch.
Send only the fields to change. state and bytes are set by processing and ignored here.
Notes.
- The response is the stored row, not the GET shape: it has no usageCount, uploadedByName or uploadSource.
- An invalid fit answers 400 with only error (no message); every other validation failure is 422 validation_error.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Media asset id. |
Request body: application/json
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | |
kind | string | no | |
url | string | no | An https URL, a /v1/ path or a data:image URI; for a web link, the page address (private and internal network addresses are refused). |
active | boolean | no | False archives the asset. |
folderId | string | null | no | |
tags | array of string | no | |
altText | string | null | no | |
thumbnailUrl | string | null | no | |
width | integer | null | no | |
height | integer | null | no | |
durationSec | number | null | no | |
startsAt | string | null | no | |
expiresAt | string | null | no | |
autoArchiveOnExpiry | boolean | no | |
qr | object | null | no | |
webConfig | object | null | no | |
nodeId | string | null | no | Move to another location (needs media.edit there). |
audioEnabled | boolean | no | |
fit | "contain" | "cover" | "fill" | "blur-fill" | no | |
baseUpdatedAt | string | no | The updatedAt your edit is based on. When set and the row has moved since, the write is refused with 409 and the current row. |
curl -X PATCH "https://api.brixsignage.com/v1/media/{id}" \
-H "Authorization: Bearer $BRIX_API_KEY" \
-H "Content-Type: application/json" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | MediaAssetUpdated | The stored row after a PATCH, without the list-only fields. |
data.id | string | Media asset id. |
data.spaceId | string | Workspace id. |
data.name | string | |
data.kind | string | Media kind: image, video, audio, pdf, powerpoint, web, dashboard, weather, rss, clock, qr, menu, directory, donor-wall, hall-of-fame, birthday-board, recognition, wayfinding, check-in, emergency, touch-kiosk, package, font. Free text in storage, so a very old row may carry another value. |
data.url | string | Where the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address. |
data.bytes | integer | Stored size in bytes (0 for links and apps). |
data.checksum | string | null | |
data.active | boolean | False when archived. |
data.folderId | string | null | |
data.tags | any | Tags — a JSON array of strings for every row written by the API (decoded from storage; left as the raw text if the stored value is not valid JSON). |
data.altText | string | null | |
data.thumbnailUrl | string | null | |
data.width | integer | null | |
data.height | integer | null | |
data.durationSec | number | null | Video/audio length in seconds. |
data.pdfPageCount | integer | null | |
data.loopDurationMs | integer | null | Animated image loop length. |
data.fontFamily | string | null | |
data.fontWeights | any | null | Font assets: the weights in the file (decoded JSON). |
data.state | string | Processing state: ready, processing, ready_with_warnings, blocked, … |
data.stateReason | string | null | |
data.stateCode | string | null | |
data.stateFault | "user" | "platform" | null | |
data.stateProgress | integer | null | |
data.codec | string | null | |
data.playableRev | integer | Bumped whenever the playable bytes change. |
data.loopState | "queued" | "ready" | "failed" | null | |
data.loopCodecs | string | null | |
data.loopRev | integer | null | |
data.loopRotation | integer | null | |
data.loopBytes | integer | null | |
data.seqState | "queued" | "ready" | "failed" | null | Playlist-sequencer rendition state (video only; minted when the video plays in a run of consecutive videos). |
data.seqCodecs | string | null | |
data.seqRev | integer | null | |
data.seqRotation | integer | null | |
data.seqBytes | integer | null | |
data.seqConform | integer | null | |
data.masterKey | string | null | |
data.masterBytes | integer | null | |
data.masterProbe | string | null | |
data.rendition4k | string | null | |
data.frameLumaMean | number | null | |
data.frameLumaVariance | number | null | |
data.frameEdgeDensity | number | null | |
data.startsAt | string | null | Plays only from this time. |
data.expiresAt | string | null | Stops playing after this time. |
data.autoArchiveOnExpiry | boolean | |
data.qr | any | null | QR overlay settings (decoded JSON). |
data.webConfig | any | null | Web link settings — refresh, zoom, header auth, … (decoded JSON). |
data.replayState | "healthy" | "replay_pending" | "replay_failed" | null | |
data.lastReplayAt | string | null | |
data.lastReplaySuccessAt | string | null | |
data.lastFailedStep | integer | null | |
data.lastFailedReason | string | null | |
data.autoCaption | boolean | |
data.captionTrackKey | string | null | |
data.captionState | "pending" | "ready" | "failed" | null | |
data.showCaptions | boolean | |
data.audioEnabled | boolean | |
data.focalRegion | string | null | Smart-fit focal region, as stored JSON text. |
data.fit | "contain" | "cover" | "fill" | "blur-fill" | How the asset fills a box. |
data.autoSmartFit | boolean | |
data.rotation | integer | Clockwise rotation in degrees. |
data.originalFormat | string | null | |
data.packageEntry | string | null | |
data.packageFiles | integer | null | |
data.packageBytes | integer | null | |
data.importSourceId | string | null | |
data.copySourceId | string | null | The partner library item this was copied from (Copy to client workspace), or null. |
data.nodeId | string | null | Home location; null = workspace library root. |
data.recalledAt | string | null | |
data.recalledBy | string | null | |
data.createdAt | string | ISO-8601 timestamp (UTC). |
data.updatedAt | string | ISO-8601 timestamp (UTC). |
data.deletedAt | string | null |
Response 400: fit is not one of contain, cover, fill, blur-fill.
Response 401: Missing, expired or revoked bearer token.
Response 403: Moving it to a location where you lack media.edit, or attaching an SSO connection without settings.edit.
Response 404: No such asset in this workspace.
Response 409: conflict: baseUpdatedAt is stale; the body carries current.
Response 422: Invalid kind, URL, folder or location.
Response 5XX: Server error. The body carries a requestId to quote to support.