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:

FieldTypeDescription
errorstringMachine-readable code: unauthorized, forbidden, not_found, validation_error, conflict, rate_limited, internal_error, …
messagestringHuman-readable explanation. Safe to show an operator.
requestIdstringPresent on 5xx: quote it to support.

GET /v1/media

Auth: Bearer token. Permission: media.view.

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.

ParameterInTypeRequiredDescription
searchquerystringnoCase-insensitive match on name or tags.
kindquerystringnoComma-separated kinds, e.g. image,video.
statequerystringnoComma-separated processing states.
folderIdquerystringnoComma-separated folder ids.
usableAtquerystringnoLocation id: only assets that may play there (own, root library, or shared in).
limitqueryintegernoPage size (max 500). Omit to get every row.
cursorquerystringnoThe nextCursor of the previous page.
countquery"1"noWith limit: also return total.
curl "https://api.brixsignage.com/v1/media" \
  -H "Authorization: Bearer $BRIX_API_KEY"

Response 200: Success.

FieldTypeDescription
dataarray of MediaAsset
data[].idstringMedia asset id.
data[].spaceIdstringWorkspace id.
data[].namestring
data[].kindstringMedia 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[].urlstringWhere the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address.
data[].bytesintegerStored size in bytes (0 for links and apps).
data[].checksumstring | null
data[].activebooleanFalse when archived.
data[].folderIdstring | null
data[].tagsanyTags — 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[].altTextstring | null
data[].thumbnailUrlstring | null
data[].widthinteger | null
data[].heightinteger | null
data[].durationSecnumber | nullVideo/audio length in seconds.
data[].pdfPageCountinteger | null
data[].loopDurationMsinteger | nullAnimated image loop length.
data[].fontFamilystring | null
data[].fontWeightsany | nullFont assets: the weights in the file (decoded JSON).
data[].statestringProcessing state: ready, processing, ready_with_warnings, blocked, …
data[].stateReasonstring | null
data[].stateCodestring | null
data[].stateFault"user" | "platform" | null
data[].stateProgressinteger | null
data[].codecstring | null
data[].playableRevintegerBumped whenever the playable bytes change.
data[].loopState"queued" | "ready" | "failed" | null
data[].loopCodecsstring | null
data[].loopRevinteger | null
data[].loopRotationinteger | null
data[].loopBytesinteger | null
data[].seqState"queued" | "ready" | "failed" | nullPlaylist-sequencer rendition state (video only; minted when the video plays in a run of consecutive videos).
data[].seqCodecsstring | null
data[].seqRevinteger | null
data[].seqRotationinteger | null
data[].seqBytesinteger | null
data[].seqConforminteger | null
data[].masterKeystring | null
data[].masterBytesinteger | null
data[].masterProbestring | null
data[].rendition4kstring | null
data[].frameLumaMeannumber | null
data[].frameLumaVariancenumber | null
data[].frameEdgeDensitynumber | null
data[].startsAtstring | nullPlays only from this time.
data[].expiresAtstring | nullStops playing after this time.
data[].autoArchiveOnExpiryboolean
data[].qrany | nullQR overlay settings (decoded JSON).
data[].webConfigany | nullWeb link settings — refresh, zoom, header auth, … (decoded JSON).
data[].replayState"healthy" | "replay_pending" | "replay_failed" | null
data[].lastReplayAtstring | null
data[].lastReplaySuccessAtstring | null
data[].lastFailedStepinteger | null
data[].lastFailedReasonstring | null
data[].autoCaptionboolean
data[].captionTrackKeystring | null
data[].captionState"pending" | "ready" | "failed" | null
data[].showCaptionsboolean
data[].audioEnabledboolean
data[].focalRegionstring | nullSmart-fit focal region, as stored JSON text.
data[].fit"contain" | "cover" | "fill" | "blur-fill"How the asset fills a box.
data[].autoSmartFitboolean
data[].rotationintegerClockwise rotation in degrees.
data[].originalFormatstring | null
data[].packageEntrystring | null
data[].packageFilesinteger | null
data[].packageBytesinteger | null
data[].importSourceIdstring | null
data[].copySourceIdstring | nullThe partner library item this was copied from (Copy to client workspace), or null.
data[].nodeIdstring | nullHome location; null = workspace library root.
data[].recalledAtstring | null
data[].recalledBystring | null
data[].createdAtstringISO-8601 timestamp (UTC).
data[].updatedAtstringISO-8601 timestamp (UTC).
data[].deletedAtstring | null
data[].usageCountintegerPlaylists, schedules, layouts, creatives, boards and screens that use this asset.
data[].uploadedByNamestring | nullWho uploaded it, from the audit log.
data[].uploadSource"ui" | "api" | "mcp" | nullHow it was uploaded.
nextCursorstring | nullPresent when ?limit was passed. Send it back as ?cursor= for the next page; null on the last page.
totalintegerTotal 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

Auth: Bearer token. Permission: media.create.

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

FieldTypeRequiredDescription
namestringyes
kindstringyes
urlstringyesAn https URL, a /v1/ path, or (for a web link) the page address. Private and internal network addresses are refused.
activebooleanno
folderIdstring | nullno
tagsarray of stringno
altTextstring | nullno
thumbnailUrlstring | nullno
widthinteger | nullno
heightinteger | nullno
durationSecnumber | nullno
startsAtstring | nullno
expiresAtstring | nullno
autoArchiveOnExpirybooleanno
qrobject | nullno
webConfigobject | nullnoWeb link settings (refresh, zoom, …).
nodeIdstring | nullnoHome location. Default: the caller's own location.
audioEnabledbooleanno
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.

FieldTypeDescription
dataobject
data.idstringMedia asset id.
data.spaceIdstringWorkspace id.
data.namestring
data.kindstringMedia 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.urlstringWhere the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address.
data.bytesintegerStored size in bytes (0 for links and apps).
data.activebooleanFalse when archived.
data.tagsanyTags — 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.statestringProcessing state: ready, processing, ready_with_warnings, blocked, …
data.autoArchiveOnExpiryboolean
data.nodeIdstring | nullHome location; null = workspace library root.
data.folderIdstring | null
data.altTextstring | null
data.thumbnailUrlstring | null
data.widthinteger | null
data.heightinteger | null
data.durationSecnumber | nullVideo/audio length in seconds.
data.startsAtstring | nullPlays only from this time.
data.expiresAtstring | nullStops playing after this time.
data.qrany | nullQR overlay settings (decoded JSON).
data.webConfigany | nullWeb link settings — refresh, zoom, header auth, … (decoded JSON).
data.audioEnabledboolean
data.fit"contain" | "cover" | "fill" | "blur-fill"How the asset fills a box.
data.createdAtstringISO-8601 timestamp (UTC).
data.updatedAtstringISO-8601 timestamp (UTC).
data.deletedAtnull

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}

Auth: Bearer token. Permission: media.view.

Return one media asset with its full details, including kind, url, dimensions, duration, tags, folder, play window, and web configuration.

ParameterInTypeRequiredDescription
idpathstringyesMedia asset id.
curl "https://api.brixsignage.com/v1/media/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY"

Response 200: Success.

FieldTypeDescription
dataMediaAssetDetailOne media asset, as GET /v1/media/{id} returns it.
data.idstringMedia asset id.
data.spaceIdstringWorkspace id.
data.namestring
data.kindstringMedia 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.urlstringWhere the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address.
data.bytesintegerStored size in bytes (0 for links and apps).
data.checksumstring | null
data.activebooleanFalse when archived.
data.folderIdstring | null
data.tagsanyTags — 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.altTextstring | null
data.thumbnailUrlstring | null
data.widthinteger | null
data.heightinteger | null
data.durationSecnumber | nullVideo/audio length in seconds.
data.pdfPageCountinteger | null
data.loopDurationMsinteger | nullAnimated image loop length.
data.fontFamilystring | null
data.fontWeightsany | nullFont assets: the weights in the file (decoded JSON).
data.statestringProcessing state: ready, processing, ready_with_warnings, blocked, …
data.stateReasonstring | null
data.stateCodestring | null
data.stateFault"user" | "platform" | null
data.stateProgressinteger | null
data.codecstring | null
data.playableRevintegerBumped whenever the playable bytes change.
data.loopState"queued" | "ready" | "failed" | null
data.loopCodecsstring | null
data.loopRevinteger | null
data.loopRotationinteger | null
data.loopBytesinteger | null
data.seqState"queued" | "ready" | "failed" | nullPlaylist-sequencer rendition state (video only; minted when the video plays in a run of consecutive videos).
data.seqCodecsstring | null
data.seqRevinteger | null
data.seqRotationinteger | null
data.seqBytesinteger | null
data.seqConforminteger | null
data.masterKeystring | null
data.masterBytesinteger | null
data.masterProbestring | null
data.rendition4kstring | null
data.frameLumaMeannumber | null
data.frameLumaVariancenumber | null
data.frameEdgeDensitynumber | null
data.startsAtstring | nullPlays only from this time.
data.expiresAtstring | nullStops playing after this time.
data.autoArchiveOnExpiryboolean
data.qrany | nullQR overlay settings (decoded JSON).
data.webConfigany | nullWeb link settings — refresh, zoom, header auth, … (decoded JSON).
data.replayState"healthy" | "replay_pending" | "replay_failed" | null
data.lastReplayAtstring | null
data.lastReplaySuccessAtstring | null
data.lastFailedStepinteger | null
data.lastFailedReasonstring | null
data.autoCaptionboolean
data.captionTrackKeystring | null
data.captionState"pending" | "ready" | "failed" | null
data.showCaptionsboolean
data.audioEnabledboolean
data.focalRegionstring | nullSmart-fit focal region, as stored JSON text.
data.fit"contain" | "cover" | "fill" | "blur-fill"How the asset fills a box.
data.autoSmartFitboolean
data.rotationintegerClockwise rotation in degrees.
data.originalFormatstring | null
data.packageEntrystring | null
data.packageFilesinteger | null
data.packageBytesinteger | null
data.importSourceIdstring | null
data.copySourceIdstring | nullThe partner library item this was copied from (Copy to client workspace), or null.
data.nodeIdstring | nullHome location; null = workspace library root.
data.recalledAtstring | null
data.recalledBystring | null
data.createdAtstringISO-8601 timestamp (UTC).
data.updatedAtstringISO-8601 timestamp (UTC).
data.deletedAtstring | null
data.usageCountintegerPlaylists, schedules, layouts, creatives, boards and screens that use this asset.
data.uploadedByNamestring | nullWho uploaded it, from the audit log.
data.uploadSource"ui" | "api" | "mcp" | nullHow it was uploaded.
data.formatstring | nullFile 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}

Auth: Bearer token. Permission: media.edit.

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.

ParameterInTypeRequiredDescription
idpathstringyesMedia asset id.

Request body: application/json

FieldTypeRequiredDescription
namestringno
kindstringno
urlstringnoAn https URL, a /v1/ path or a data:image URI; for a web link, the page address (private and internal network addresses are refused).
activebooleannoFalse archives the asset.
folderIdstring | nullno
tagsarray of stringno
altTextstring | nullno
thumbnailUrlstring | nullno
widthinteger | nullno
heightinteger | nullno
durationSecnumber | nullno
startsAtstring | nullno
expiresAtstring | nullno
autoArchiveOnExpirybooleanno
qrobject | nullno
webConfigobject | nullno
nodeIdstring | nullnoMove to another location (needs media.edit there).
audioEnabledbooleanno
fit"contain" | "cover" | "fill" | "blur-fill"no
baseUpdatedAtstringnoThe 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.

FieldTypeDescription
dataMediaAssetUpdatedThe stored row after a PATCH, without the list-only fields.
data.idstringMedia asset id.
data.spaceIdstringWorkspace id.
data.namestring
data.kindstringMedia 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.urlstringWhere the bytes live: a /v1/media/<id>/file path, an https URL, r2://…, or (for a web link) the page address.
data.bytesintegerStored size in bytes (0 for links and apps).
data.checksumstring | null
data.activebooleanFalse when archived.
data.folderIdstring | null
data.tagsanyTags — 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.altTextstring | null
data.thumbnailUrlstring | null
data.widthinteger | null
data.heightinteger | null
data.durationSecnumber | nullVideo/audio length in seconds.
data.pdfPageCountinteger | null
data.loopDurationMsinteger | nullAnimated image loop length.
data.fontFamilystring | null
data.fontWeightsany | nullFont assets: the weights in the file (decoded JSON).
data.statestringProcessing state: ready, processing, ready_with_warnings, blocked, …
data.stateReasonstring | null
data.stateCodestring | null
data.stateFault"user" | "platform" | null
data.stateProgressinteger | null
data.codecstring | null
data.playableRevintegerBumped whenever the playable bytes change.
data.loopState"queued" | "ready" | "failed" | null
data.loopCodecsstring | null
data.loopRevinteger | null
data.loopRotationinteger | null
data.loopBytesinteger | null
data.seqState"queued" | "ready" | "failed" | nullPlaylist-sequencer rendition state (video only; minted when the video plays in a run of consecutive videos).
data.seqCodecsstring | null
data.seqRevinteger | null
data.seqRotationinteger | null
data.seqBytesinteger | null
data.seqConforminteger | null
data.masterKeystring | null
data.masterBytesinteger | null
data.masterProbestring | null
data.rendition4kstring | null
data.frameLumaMeannumber | null
data.frameLumaVariancenumber | null
data.frameEdgeDensitynumber | null
data.startsAtstring | nullPlays only from this time.
data.expiresAtstring | nullStops playing after this time.
data.autoArchiveOnExpiryboolean
data.qrany | nullQR overlay settings (decoded JSON).
data.webConfigany | nullWeb link settings — refresh, zoom, header auth, … (decoded JSON).
data.replayState"healthy" | "replay_pending" | "replay_failed" | null
data.lastReplayAtstring | null
data.lastReplaySuccessAtstring | null
data.lastFailedStepinteger | null
data.lastFailedReasonstring | null
data.autoCaptionboolean
data.captionTrackKeystring | null
data.captionState"pending" | "ready" | "failed" | null
data.showCaptionsboolean
data.audioEnabledboolean
data.focalRegionstring | nullSmart-fit focal region, as stored JSON text.
data.fit"contain" | "cover" | "fill" | "blur-fill"How the asset fills a box.
data.autoSmartFitboolean
data.rotationintegerClockwise rotation in degrees.
data.originalFormatstring | null
data.packageEntrystring | null
data.packageFilesinteger | null
data.packageBytesinteger | null
data.importSourceIdstring | null
data.copySourceIdstring | nullThe partner library item this was copied from (Copy to client workspace), or null.
data.nodeIdstring | nullHome location; null = workspace library root.
data.recalledAtstring | null
data.recalledBystring | null
data.createdAtstringISO-8601 timestamp (UTC).
data.updatedAtstringISO-8601 timestamp (UTC).
data.deletedAtstring | 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.