Skip to content

Media Assets API#

Read one asset from your tenant's Media Libraries: its file, its library, its metadata, and the plays it shows where its source declared them. Each response carries the same version the content changes feed reports for the asset, so you can tell whether you have already handled this state of it.

Endpoint Summary#

Endpoint Description
GET /api/media-assets/by-id/{id} One Media Library asset by its id, with its version

Get a Media Asset (GET /api/media-assets/by-id/{id})#

GET /api/media-assets/by-id/{id}
x-storyteller-api-key: your-api-key-here

id is the asset's id, as a mediaAsset item in the content changes feed gives it. The asset must be in your tenant, which the API key decides.

Successful responses carry Cache-Control: private, no-store.

Example Response#

A file uploaded in Studio while editing a Story:

{
  "id": "33333333-3333-4333-8333-333333333333",
  "version": "00000000002C9A57",
  "library": {
    "id": "44444444-4444-4444-8444-444444444444",
    "providerCode": "MEDIA"
  },
  "title": "Brown dunk",
  "fileUrl": "https://cdn.example.com/assets/studio-uploads/3333/source.mp4",
  "thumbnailUrl": "https://cdn.example.com/assets/studio-uploads/3333/source.jpg",
  "mimeType": "video/mp4",
  "width": 1080,
  "height": 1920,
  "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "createdAt": "2026-10-20T23:50:00Z",
  "metadata": [
    { "name": "filetype", "value": "video/mp4" },
    { "name": "height", "value": "1920" },
    { "name": "sha256", "value": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" },
    { "name": "studioPageId", "value": "66666666-6666-4666-8666-666666666666" },
    { "name": "studioStoryId", "value": "55555555-5555-4555-8555-555555555555" },
    { "name": "title", "value": "Brown dunk" },
    { "name": "width", "value": "1080" }
  ],
  "source": {
    "kind": "studioUpload",
    "sourceAssetId": null,
    "revision": null,
    "rendition": "9:16",
    "events": [],
    "uploadKeys": ["tenants/77777777-7777-4777-8777-777777777777/2026/295/upload/source.mp4"]
  },
  "uploadedFor": {
    "storyId": "55555555-5555-4555-8555-555555555555",
    "clipId": null,
    "pageId": "66666666-6666-4666-8666-666666666666"
  },
  "legacyMediaExternalId": null
}

An asset a media provider delivered reads source.kind provider, with the provider's own id as source.sourceAssetId and, where the provider declared them, the plays it shows in source.events:

"events": [
  {
    "game": { "provider": "stats-feed", "id": "game-1001" },
    "play": { "provider": "stats-feed", "id": "11" },
    "actionType": "Dunk",
    "period": 2,
    "gameClock": "7:34",
    "eventTimeUtc": "2026-10-20T23:45:12Z"
  }
]

Response Fields#

Field Type Description
id string The asset's id
version string or null The version the content changes feed reports for this state of the asset. Opaque: compare it for equality. Null if the asset has not been recorded in the feed. It moves when anything below changes, except the URLs (see fileUrl).
library object or null The Media Library the asset is in: its id, and providerCode, the library's provider, such as MEDIA. Null if it is in none.
title string or null The asset's title
fileUrl string or null The file's URL. Built with your tenant's CDN address when you read it, so a change of CDN address changes it without changing version.
thumbnailUrl string or null The thumbnail's URL, where the asset has one, built as fileUrl is
mimeType string or null The file's MIME type, where Storyteller holds it
width, height integer or null The file's dimensions in pixels, where Storyteller holds them
sha256 string or null The file's SHA-256, where Storyteller holds it (Studio uploads)
createdAt string When the asset was added, UTC
metadata array Every metadata entry on the asset, as name and value, in name order. A duration entry, where present, is in the unit its source used.
source.kind string provider when Storyteller holds a media provider's own id for the asset (source.sourceAssetId); studioUpload when the asset's metadata carries the record Studio keeps for a file uploaded while editing a Story or Clip (its sha256 and the studioStoryId or studioClipId); otherwise other. Both are read from the asset's own metadata, so an import that writes the same entries reads the same. An asset synced from a provider whose id Storyteller does not hold this way reads other. Treat an unknown value as other.
source.sourceAssetId string or null The provider's own id for the asset
source.revision string or null Reserved for the provider's revision of the asset; null for now
source.rendition string or null The aspect ratio in lowest terms, such as 9:16; null without both dimensions
source.events array The plays the asset shows, as its source declared them, in order: game and play (each provider and id, or null), actionType, period, gameClock (minutes and seconds left, such as 7:34) and eventTimeUtc. Ids and providers are exactly as the source gave them: an event whose id or provider is longer than Storyteller keeps (256 characters for a play id) is left out rather than shortened, and a longer actionType or gameClock reads null. At most 200 are kept. Empty when the source declared none, and for assets added before Storyteller kept them. These are what the source supplied: the league's own data is authoritative.
source.uploadKeys array of strings The storage keys of the Studio uploads this asset holds, in ordinal order: the file it was captured from, and each later upload of the same file, which adds its key rather than a new asset. A Studio page or Clip that uses an upload refers to it by this key. Keys are only added, and adding one is a change to the asset. The list holds every key at the asset's version, and may also hold keys added since, each of which arrives as its own later change. Empty when there are none, including for assets captured before keys were recorded.
uploadedFor object or null For a Studio upload, the storyId or clipId, and the pageId, it was uploaded for; otherwise null. Uploading a file that is already in your Studio uploads library adds no new asset, so uploadedFor keeps the Story or Clip it was first uploaded for.
legacyMediaExternalId string or null The id Storyteller's earlier media delivery gave this asset, so a system that already holds it can find it; null if it was never delivered that way

New fields may be added; ignore any you do not recognise.

Error Handling#

All errors are RFC 7807 Problem Details.

404 Not Found when the asset does not exist, has been deleted, or is not in your tenant:

{
  "status": 404,
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
  "title": "Not Found",
  "detail": "Media asset with id 33333333-3333-4333-8333-333333333333 not found",
  "instance": ""
}

A mediaAsset item that the feed lists as deleted reads as 404.

401 Unauthorized without a valid Server App key (see Authentication).