Skip to main content
Media stores files for content, pages, playlists, community attachments, and public embeds.

Upload as an admin

Admin media routes are team-scoped:
POST   /api/v1/teams/{team_id}/files                        create file row + get presigned S3 PUT URL
POST   /api/v1/teams/{team_id}/files/{file_id}/finalize     confirm upload, transition PENDING → READY
GET    /api/v1/teams/{team_id}/files
GET    /api/v1/teams/{team_id}/files/{file_id}
PATCH  /api/v1/teams/{team_id}/files/{file_id}
DELETE /api/v1/teams/{team_id}/files/{file_id}
The upload flow creates a file row and returns a presigned S3 PUT URL in the same response (POST /files). Upload directly to S3 using that URL, then call /finalize to confirm the upload succeeded and begin processing.

Organize files

Folders and playlists help structure media libraries:
GET  /api/v1/teams/{team_id}/folders
POST /api/v1/teams/{team_id}/folders
GET  /api/v1/teams/{team_id}/playlists
POST /api/v1/teams/{team_id}/playlists

Use media in pages and content

Attachment routes connect media to page sections, hub pages, playlist covers, and other targets. The generated API reference lists each attachment route under media-admin-attachments.

Member uploads

Members can request upload URLs for member-owned uploads:
POST /api/v1/hub/{hub_id}/files/upload-url

Search and transcripts

Media search supports title, description, transcript, and semantic search when embeddings are enabled.
GET /api/v1/teams/{team_id}/search/media
GET /api/v1/hub/{hub_id}/search/media
GET /api/v1/teams/{team_id}/media/{media_id}/transcript
GET /api/v1/teams/{team_id}/media/{media_id}/transcript.vtt

Image variant URLs and expiry

Imgproxy variant URLs are signed and time-limited. The default TTL is 24 hours. After the URL expires imgproxy rejects the request regardless of the signature. The API returns a variants_expires_at timestamp alongside the variants map, and cover_url_expires_at alongside cover_url on playlist resources, so clients know when to refresh:
{
  "data": {
    "attributes": {
      "variants": {
        "small-400": "https://imgproxy.member.dev/<signature>/exp:1749740400/rs:fill:400/...",
        "large-1440": "https://imgproxy.member.dev/<signature>/exp:1749740400/rs:fill:1440/..."
      },
      "variants_expires_at": "2026-06-10T14:00:00Z",
      "cover_url": "https://imgproxy.member.dev/<signature>/exp:1749740400/...",
      "cover_url_expires_at": "2026-06-10T14:00:00Z",
      "expires_at": "2026-06-10T14:05:00Z"
    }
  }
}
expires_at describes the playback_url (CloudFront, ~300 s TTL). variants_expires_at describes the imgproxy variants map (24–48 h TTL). Use the right timestamp for each URL type. The exp:{unix_timestamp} component is sealed inside the HMAC signature. imgproxy rejects any request whose current clock time exceeds the embedded expiry. Client guidance: do not cache imgproxy URLs (variants, cover) beyond their *_expires_at companion. Re-fetch the media or playlist resource to get a fresh set of signed URLs. This closes the “leaked URL grants permanent access” risk class — a URL that leaks after its expiry is unusable. Avatar URLs on community and discussion/comment resources also carry matching expiry fields (photo_url_expires_at, photo_thumbnail_url_expires_at, author_photo_url_expires_at, author_photo_thumbnail_url_expires_at). Null means no image or unsigned dev mode; re-fetch the parent resource for a fresh URL.

HLS video playback and session expiry

READY transcoded videos are served via CloudFront signed HLS URLs. The signed custom policy grants access to every playlist and segment file under the video’s media folder for the TTL window (default: 4 hours). GET /file/{file_id}/media returns data.meta.playback for READY videos:
{
  "data": {
    "type": "media_playback",
    "meta": {
      "playback": {
        "kind": "video",
        "hls_manifest": "https://cdn.member.dev/team/media/media/hls/master.m3u8?Policy=...&Signature=...&Key-Pair-Id=...",
        "hls_expires_at": "2026-06-12T10:12:25+00:00",
        "duration_seconds": 240,
        "captions": []
      }
    }
  }
}
The hls_expires_at field is the exact UTC datetime when the CloudFront signed policy expires. CloudFront validates the policy on every HLS child playlist and segment request, so a long video will fail mid-playback if the policy expires before the player finishes. Client guidance: refresh the playback payload (re-call GET /file/{file_id}/media) before hls_expires_at. Caption URLs share the same TTL and are refreshed alongside the manifest. The player must propagate the Policy, Signature, and Key-Pair-Id query parameters from hls_manifest to all child HLS requests (quality playlists and .ts segment files).