[Objects](https://oods-foundry.com/objects) / Media

# Media

Canonical digital asset metadata powering media library, DAM, and CMS integrations.

Maturity not declared 4 traits · 7 contexts · 29 fields [Media.object.yaml](#file) [How objects work](https://oods-foundry.com/guides/objects-and-traits)

Generated from @oods/foundry 0.10.1

## Screens

Every screen below was generated from this object by OODS Foundry 0.10.1, in React and Vue, and shows the calls that made it and its content hash. Each runs as its generated app runs, in this site's brand, Aquex, in the theme you choose, with the sample records this object's own file carries. A value no record gives shows as a neutral one, such as an empty value or "Not recorded"; OODS Foundry invents none.

[Media detail screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/detail-react)

React app · sample records Maturity: not declared `sha256:320e00007098…` [Open full size: Media detail screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/detail-react)

Vue app · sample records Maturity: not declared `sha256:ae41c8c75034…` [Open full size: Media detail screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/detail-vue)

React app · sample records Maturity: not declared `sha256:d11a9dcc515e…` [Open full size: Media list screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/list-react)

Vue app · sample records Maturity: not declared `sha256:64d4b2279a0f…` [Open full size: Media list screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/list-vue)

React app · sample records Maturity: not declared `sha256:da1fb9c7ef1b…` [Open full size: Media form screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/form-react)

Vue app · sample records Maturity: not declared `sha256:25f98e320842…` [Open full size: Media form screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/form-vue)

React app · sample records Maturity: not declared `sha256:b36fbe1706ed…` [Open full size: Media timeline screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/timeline-react)

Vue app · sample records Maturity: not declared `sha256:fe2ad776bf17…` [Open full size: Media timeline screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/timeline-vue)

React app · sample records Maturity: not declared `sha256:489dfdb84dc3…` [Open full size: Media card screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/card-react)

Vue app · sample records Maturity: not declared `sha256:ac43399fc348…` [Open full size: Media card screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/card-vue)

React app · sample records Maturity: not declared `sha256:f28b2dbb9a91…` [Open full size: Media inline screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/inline-react)

Vue app · sample records Maturity: not declared `sha256:610d02ce9b01…` [Open full size: Media inline screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/inline-vue)

React app · sample records Maturity: not declared `sha256:b0bd2df52d16…` [Open full size: Media workflow screen generated by OODS Foundry in React](https://oods-foundry.com/objects/media/screens/workflow-react)

Vue app · sample records Maturity: not declared `sha256:d5fe04e86034…` [Open full size: Media workflow screen generated by OODS Foundry in Vue](https://oods-foundry.com/objects/media/screens/workflow-vue)

The calls that made the Media detail apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "detail",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-df449a11",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-df449a11",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:320e000070989bcb9e52508647debde1a268718833271dddf20f277d76d33f9a`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:ae41c8c750347a86027d9e4733556fd19a6a00dce9979b55d24ffa8f6b0ca004`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Media list apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "list",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-8654c817",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-8654c817",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:d11a9dcc515e130f905027d94979784b8ba111a22b9587857bc8e4d4880ac4c2`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:64d4b2279a0f9edf859d50b381d7f0bcb63a15125756b01c7f81a9fe27e27b98`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Media form apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "form",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-4567fdbb",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-4567fdbb",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:da1fb9c7ef1be1fd1af9d755e8df775e98b8001ef3d3184d388426aef2c34abd`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:25f98e320842e1f02a19f187be5ae4010efed274c09b4135a15a2f2f411619de`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Media timeline apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "timeline",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-ee926d78",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-ee926d78",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:b36fbe1706edf38d478252634d753d902a17a84b08f8c9b244c69c4761f7b358`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:fe2ad776bf17430b43b6dc17024e6d8a7b96f52611b1394dc7cd23660933f1a6`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Media card apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "card",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-9da481e3",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-9da481e3",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:489dfdb84dc3f944347a6b5c4fdd3d9ba23e219061ee041cd4dea014435405bb`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:ac43399fc3487c6bc8004dc6372b74c9e9f0a9c116e9a07bc75fbab655d61b7a`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Media inline apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "inline",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-cd750fe0",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-cd750fe0",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:f28b2dbb9a917226936bf14e9e5d7ecaf58e70e5715b3e669f78b5f67012c326`. Its `src/` folder: App.tsx, GeneratedUI.tsx, app.css, main.tsx, oods-brand-aquex.css

The Vue app's content hash: `sha256:610d02ce9b011c2e46150cec59dead0d8bd200eb52fbd1e2778a0e190898c92d`. Its `src/` folder: App.vue, GeneratedUI.vue, app.css, main.ts, oods-brand-aquex.css

The calls that made the Media workflow apps, in React and Vue

3 calls, as the pipeline sent them

```
design_compose {
  "object": "Media",
  "context": "workflow",
  "preferences": {
    "brand": "Aquex",
    "theme": "light"
  }
}

code_generate {
  "schemaRef": "compose-d3ba52ce",
  "framework": "react",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}

code_generate {
  "schemaRef": "compose-d3ba52ce",
  "framework": "vue",
  "profile": "build",
  "options": {
    "output": "application",
    "brand": "Aquex",
    "theme": "light",
    "payloadMode": "file"
  }
}
```

The React app's content hash: `sha256:b0bd2df52d1660f638d25c98111ff36537a151c45e5f275e5a0b1a8e1f12c6ab`. Its `src/` folder: App.tsx, actions.ts, app.css, application.ts, main.tsx, oods-brand-aquex.css, sample-data.ts, screens/, ssr.tsx, store.ts

The Vue app's content hash: `sha256:d5fe04e860349eec48e8740770ceb5d58de489c741b0ec50a2d9bc11f3c20375`. Its `src/` folder: App.vue, actions.ts, app.css, application.ts, main.ts, oods-brand-aquex.css, sample-data.ts, screens/, ssr.ts, store.ts

## Fields

| Field | Type | Description |
| - | - | - |
| `label` required | string | Human-readable display name rendered in primary surfaces. |
| `description` | string | Supporting description used in detail and card contexts. |
| `placeholder` | string | Hint copy surfaced in form fields when the label is empty. |
| `status` required | string | Canonical lifecycle state derived from the states parameter.MoreThis is the single source of truth for the entity's current lifecycle position. Consumed by Colorized to resolve visual tokens, and by view extensions to render StatusBadge and StatusTimeline. |
| `state_history` | StateTransition\[] | Chronological log of state transitions.MoreEach entry records the before/after states, timestamp, and (when governance is enabled) the actor, reason, and transition metadata. Rendered by StatusTimeline in the detail and timeline views. Entry structure: - from: string (previous state) - to: string (new state) - timestamp: ISO 8601 datetime - actor_id: string (user/system who triggered the transition, optional) - reason: string (human-readable justification, required when requireTransitionReason is true) - transition_metadata: Record\<string, unknown> (arbitrary context, optional) |
| `allowed_transitions` | string\[] | Materialized list of valid next states from the current status, computed from the transitionRules parameter.MoreWhen transitionRules is null (open model), this contains all states except the current one. Used by StatusSelector to disable invalid options and by StatusBadge to indicate available paths. |
| `created_at` required | datetime | Timestamp recording when the entity was first created. |
| `updated_at` | datetime | Timestamp for the most recent modification, when available. |
| `last_event` required | string | Lifecycle event associated with the most recent timestamp mutation. |
| `last_event_at` | datetime | Timestamp for the lifecycle event captured in last_event. |
| `classification_metadata` required | ClassificationMetadata | Operational metadata describing mode, storage model, governance rules, and audit timestamps. |
| `categories` | CategoryNode\[] | Ordered taxonomy nodes scoped to the object. |
| `primary_category_id` | string | Identifier of the canonical taxonomy node. |
| `primary_category_path` | string | Human-readable breadcrumb path (Electronics > Mobile > Android). |
| `tags` | Tag\[] | Canonical tag collection after synonym collapse. |
| `tag_count` | number | Number of canonical tags assigned to the object. |
| `tag_preview` | string | Denormalized comma-delimited preview for list renders. |
| `media_id` required | uuid | Primary identifier for the media asset across services. |
| `asset_type` required | string (image, video, audio, document) | Media form factor guiding viewer presentation. |
| `mime_type` required | string | IANA MIME type recorded at ingestion. |
| `storage_bucket` required | string | Storage namespace (S3 bucket, GCS bucket) backing the asset. |
| `object_key` required | string | Provider-specific path or key referencing the binary object. |
| `source_url` | string | Canonical CDN URL for public consumption. |
| `checksum` | string | Hex-encoded checksum (SHA-256) for deduplication. |
| `file_size_bytes` required | number | Size of the binary payload in bytes. |
| `width_px` | number | Pixel width for images/videos when available. |
| `height_px` | number | Pixel height for images/videos when available. |
| `duration_seconds` | number | Media playback duration for video/audio assets. |
| `transcoding_profiles` | string\[] | List of transcoding profiles generated for the asset. |

## Traits

Each trait adds fields and behaviour. Every object with the trait gets the same.

- [Labelled as MediaLabel](https://oods-foundry.com/traits/labelled)
- [Stateful as MediaLifecycle](https://oods-foundry.com/traits/stateful)
- [Timestampable as MediaTimestamps](https://oods-foundry.com/traits/timestampable)
- [Classifiable as MediaClassification](https://oods-foundry.com/traits/classifiable)

## Relationships

These arrows show declared relationships between object types. They don't join live records.

## The object file

The Media object definition in YAML: `objects/content/Media.object.yaml`

```
object:
  name: Media
  version: 1.0.0
  domain: content.asset
  description: Canonical digital asset metadata powering media library, DAM, and CMS integrations.
  tags:
    - content
    - media
    - asset
    - classifiable

traits:
  - name: content/Labelled
    alias: MediaLabel
  - name: lifecycle/Stateful
    alias: MediaLifecycle
    parameters:
      states:
        - ingested
        - processing
        - available
        - deprecated
      initialState: ingested
  - name: lifecycle/Timestampable
    alias: MediaTimestamps
    parameters:
      recordedEvents:
        - ingested
        - transcoded
        - published
        - archived
      timezone: UTC
  - name: core/Classifiable
    alias: MediaClassification
    parameters:
      classification_mode: tag
      tag_policy: open
      max_tags: 32

schema:
  media_id:
    type: uuid
    required: true
    description: Primary identifier for the media asset across services.
  asset_type:
    type: string
    required: true
    description: Media form factor guiding viewer presentation.
    validation:
      enum:
        - image
        - video
        - audio
        - document
  mime_type:
    type: string
    required: true
    description: IANA MIME type recorded at ingestion.
  storage_bucket:
    type: string
    required: true
    description: Storage namespace (S3 bucket, GCS bucket) backing the asset.
  object_key:
    type: string
    required: true
    description: Provider-specific path or key referencing the binary object.
  source_url:
    type: string
    required: false
    description: Canonical CDN URL for public consumption.
  checksum:
    type: string
    required: false
    description: Hex-encoded checksum (SHA-256) for deduplication.
  file_size_bytes:
    type: number
    required: true
    description: Size of the binary payload in bytes.
    validation:
      minimum: 0
  width_px:
    type: number
    required: false
    description: Pixel width for images/videos when available.
    validation:
      minimum: 1
  height_px:
    type: number
    required: false
    description: Pixel height for images/videos when available.
    validation:
      minimum: 1
  duration_seconds:
    type: number
    required: false
    description: Media playback duration for video/audio assets.
    validation:
      minimum: 0
  transcoding_profiles:
    type: string[]
    required: false
    description: List of transcoding profiles generated for the asset.
    default: []

semantics:
  # The one-line summary under the asset's name in lists (content/Labelled's description, marked as the summary).
  description:
    semantic_type: text.summary
    token_mapping: tokenMap(text.body.*)
    ui_hints:
      component: TextBody
      maxLengthParameter: maxDescriptionLength
  media_id:
    semantic_type: media.asset.id
    token_mapping: tokenMap(media.asset.id)
  asset_type:
    semantic_type: media.asset.type
    token_mapping: tokenMap(media.asset.type)
  mime_type:
    semantic_type: media.asset.mime
    token_mapping: tokenMap(media.asset.mime)
  file_size_bytes:
    semantic_type: media.asset.size
    token_mapping: tokenMap(metric.storage.bytes)
  duration_seconds:
    semantic_type: media.asset.duration
    token_mapping: tokenMap(metric.duration.media)

tokens:
  media.asset.id: "var(--sys-text-strong)"
  media.asset.type: "var(--sys-badge-neutral-fg)"
  media.asset.mime: "var(--sys-text-muted)"
  metric.storage.bytes: "var(--sys-text-subtle)"
  metric.duration.media: "var(--sys-text-subtle)"

metadata:
  owners:
    - media-platform@oods.systems
    - dam@oods.systems
  steward: experience@oods.systems
  changelog:
    - version: 1.0.0
      date: "2025-11-18"
      description: Initial Media object with tag-mode Classifiable integration for asset folksonomy.

# Eight authored sample assets from the help-centre media library: diagrams, a release video, a webinar recording and
# PDFs. Each follows the asset lifecycle from ingestion (its initial state); sizes, dimensions and durations fit the
# asset type, and the last event is the latest recorded change.
samples:
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c01
    label: Proration timeline diagram
    description: A mid-cycle upgrade, day by day
    asset_type: image
    mime_type: image/png
    storage_bucket: help-centre-assets
    object_key: articles/prorations/proration-timeline.png
    source_url: https://cdn.example/help-centre/articles/prorations/proration-timeline.png
    checksum: 9f2c4e6a8b0d1f3e5a7c9e1b3d5f7a9c2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e
    file_size_bytes: 214380
    width_px: 1600
    height_px: 900
    duration_seconds: null
    transcoding_profiles: [webp-1600, webp-800]
    tag_preview: diagram, billing
    status: available
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-07-01T10:00:00Z', reason: Uploaded by Marcus Hale }
      - { title: Processing, from: ingested, to: processing, at: '2026-07-01T10:00:10Z', reason: Generating web sizes }
      - { title: Available, from: processing, to: available, at: '2026-07-01T10:01:00Z', reason: Published to the CDN }
    created_at: '2026-07-01T10:00:00Z'
    updated_at: '2026-07-01T10:01:00Z'
    last_event: published
    last_event_at: '2026-07-01T10:01:00Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c02
    label: Payment retry schedule
    description: Four retries over 21 days
    asset_type: image
    mime_type: image/svg+xml
    storage_bucket: help-centre-assets
    object_key: articles/retries/retry-schedule.svg
    source_url: https://cdn.example/help-centre/articles/retries/retry-schedule.svg
    checksum: 1b3d5f7a9c2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f3e5a7c9e
    file_size_bytes: 18944
    width_px: 1200
    height_px: 675
    duration_seconds: null
    transcoding_profiles: []
    tag_preview: diagram, payments
    status: available
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-08-12T09:30:00Z', reason: Uploaded by Ines Duarte }
      - { title: Processing, from: ingested, to: processing, at: '2026-08-12T09:30:05Z', reason: Checking the SVG }
      - { title: Available, from: processing, to: available, at: '2026-08-12T09:30:20Z', reason: Published to the CDN }
    created_at: '2026-08-12T09:30:00Z'
    updated_at: '2026-08-12T09:30:20Z'
    last_event: published
    last_event_at: '2026-08-12T09:30:20Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c03
    label: September release walkthrough
    description: A 3-minute tour of the new features
    asset_type: video
    mime_type: video/mp4
    storage_bucket: help-centre-video
    object_key: releases/2026-09/walkthrough.mp4
    source_url: https://cdn.example/help-centre/releases/2026-09/walkthrough.m3u8
    checksum: 3e5a7c9e1b3d5f7a9c2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f
    file_size_bytes: 48213776
    width_px: 1920
    height_px: 1080
    duration_seconds: 212
    transcoding_profiles: [hls-1080p, hls-720p, hls-480p]
    tag_preview: video, releases
    status: available
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-09-28T15:00:00Z', reason: Uploaded by Elena Park }
      - { title: Processing, from: ingested, to: processing, at: '2026-09-28T15:01:00Z', reason: Transcoding three sizes }
      - { title: Available, from: processing, to: available, at: '2026-09-28T15:14:00Z', reason: Transcoding finished }
    created_at: '2026-09-28T15:00:00Z'
    updated_at: '2026-09-28T15:14:00Z'
    last_event: transcoded
    last_event_at: '2026-09-28T15:14:00Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c04
    label: Usage-based billing launch banner
    description: Hero image for the October launch
    asset_type: image
    mime_type: image/webp
    storage_bucket: help-centre-assets
    object_key: articles/usage-billing/launch-banner.webp
    source_url: null
    checksum: 5f7a9c2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f3e5a7c9e1b3d
    file_size_bytes: 356210
    width_px: 2400
    height_px: 1260
    duration_seconds: null
    transcoding_profiles: []
    tag_preview: banner, launch
    status: processing
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-09-24T11:00:00Z', reason: Uploaded by Elena Park }
      - { title: Processing, from: ingested, to: processing, at: '2026-09-24T11:01:00Z', reason: Waiting for brand approval }
    created_at: '2026-09-24T11:00:00Z'
    updated_at: '2026-09-24T11:01:00Z'
    last_event: ingested
    last_event_at: '2026-09-24T11:00:00Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c05
    label: Billing Q&A webinar
    description: Audio from the August customer webinar
    asset_type: audio
    mime_type: audio/mpeg
    storage_bucket: help-centre-audio
    object_key: webinars/2026-08/billing-qa.mp3
    source_url: https://cdn.example/help-centre/webinars/2026-08/billing-qa.mp3
    checksum: 7a9c2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f3e5a7c9e1b3d5f
    file_size_bytes: 43920000
    duration_seconds: 2745
    width_px: null
    height_px: null
    transcoding_profiles: [mp3-128k]
    tag_preview: webinar, audio
    status: available
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-08-27T18:00:00Z', reason: Uploaded by Marcus Hale }
      - { title: Processing, from: ingested, to: processing, at: '2026-08-27T18:00:30Z', reason: Normalising loudness }
      - { title: Available, from: processing, to: available, at: '2026-08-27T18:06:00Z', reason: Transcoding finished }
    created_at: '2026-08-27T18:00:00Z'
    updated_at: '2026-08-27T18:06:00Z'
    last_event: transcoded
    last_event_at: '2026-08-27T18:06:00Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c06
    label: Invoice template
    description: Branded PDF invoice layout
    asset_type: document
    mime_type: application/pdf
    storage_bucket: help-centre-assets
    object_key: templates/invoice-template.pdf
    source_url: https://cdn.example/help-centre/templates/invoice-template.pdf
    checksum: 9c2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f3e5a7c9e1b3d5f7a
    file_size_bytes: 184320
    width_px: null
    height_px: null
    duration_seconds: null
    transcoding_profiles: []
    tag_preview: template, invoices
    status: available
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-09-09T08:00:00Z', reason: Uploaded by Marcus Hale }
      - { title: Processing, from: ingested, to: processing, at: '2026-09-09T08:00:05Z', reason: Scanning the PDF }
      - { title: Available, from: processing, to: available, at: '2026-09-09T08:00:40Z', reason: Published to the CDN }
    created_at: '2026-09-09T08:00:00Z'
    updated_at: '2026-09-09T08:00:40Z'
    last_event: published
    last_event_at: '2026-09-09T08:00:40Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c07
    label: Pro plan pricing sheet (2025)
    description: Pricing for the retired Pro plan
    asset_type: document
    mime_type: application/pdf
    storage_bucket: help-centre-assets
    object_key: pricing/pro-plan-2025.pdf
    source_url: https://cdn.example/help-centre/pricing/pro-plan-2025.pdf
    checksum: 2e4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f3e5a7c9e1b3d5f7a9c
    file_size_bytes: 96256
    width_px: null
    height_px: null
    duration_seconds: null
    transcoding_profiles: []
    tag_preview: pricing, legacy
    status: deprecated
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2025-10-06T09:00:00Z', reason: Uploaded by Elena Park }
      - { title: Processing, from: ingested, to: processing, at: '2025-10-06T09:00:05Z', reason: Scanning the PDF }
      - { title: Available, from: processing, to: available, at: '2025-10-06T09:00:30Z', reason: Published to the CDN }
      - { title: Deprecated, from: available, to: deprecated, at: '2026-04-30T09:00:00Z', reason: Pro plan migration complete }
    created_at: '2025-10-06T09:00:00Z'
    updated_at: '2026-04-30T09:00:00Z'
    last_event: archived
    last_event_at: '2026-04-30T09:00:00Z'
  - media_id: 5a7c9e1b-3d5f-4a6c-8e0a-2b4d6f8a0c08
    label: Product tour (raw upload)
    description: Needs a re-upload after a failed transcode
    asset_type: video
    mime_type: video/quicktime
    storage_bucket: help-centre-video
    object_key: tours/product-tour-raw.mov
    source_url: null
    checksum: 4a6c8e0b2d4f6a8c0e2b4d6f8a0c1e9f2c4e6a8b0d1f3e5a7c9e1b3d5f7a9c2e
    file_size_bytes: 612400112
    width_px: 1920
    height_px: 1080
    duration_seconds: 95
    transcoding_profiles: []
    tag_preview: video, tour
    status: ingested
    state_history:
      - { title: Uploaded, from: null, to: ingested, at: '2026-09-26T13:00:00Z', reason: Uploaded by Elena Park }
      - { title: Processing, from: ingested, to: processing, at: '2026-09-26T13:02:00Z', reason: Transcoding three sizes }
      - { title: Transcode failed, from: processing, to: ingested, at: '2026-09-26T13:20:00Z', reason: Unsupported codec; re-upload requested }
    created_at: '2026-09-26T13:00:00Z'
    updated_at: '2026-09-26T13:20:00Z'
    last_event: ingested
    last_event_at: '2026-09-26T13:20:00Z'
```
