Shukr Docs
Shukr Public Media Service

Concepts

The media asset data model, allowed MIME types, upload modes, streaming providers, and the stream lifecycle.

This page is the reference for the shapes and enums you'll see throughout the API. Endpoint pages link back here rather than repeating them.

The media asset

A MediaAsset is echoed by almost every endpoint (under data.asset, data.items[], etc.). All fields are always present; nullable fields are null until they apply.

FieldTypeNotes
iduuidPrimary key.
owner_user_idstringThe owning Shukr user.
object_keystringStorage key in R2 (unique).
original_filenamestringAs supplied at upload time.
content_type"place" | "post"Business category, not a MIME type — where the media is used.
mime_typestringThe real MIME type, e.g. image/jpeg. Must be allowed.
file_size_bytesnumber | nullKnown size, when provided.
status"pending" | "uploaded" | "deleted"Upload lifecycle.
visibility"public" | "private"Defaults to public.
external_refstring | nullYour own correlation id (e.g. a place or post id).
etagstring | nullR2 ETag after a completed upload.
asset_kind"file" | "stream"File vs. video — see Overview.
stream_provider"bunny" | "onprem" | "cloudflare" | nullSet for stream assets.
provider_asset_idstring | nullThe provider's id for the video.
stream_status"pending_upload" | "processing" | "ready" | "failed" | nullTranscode lifecycle.
playbackPlaybackManifest | nullPlayback URLs once ready.
object_upload_mode"single_presigned_put" | "multipart_presigned_put" | "tus" | nullHow the bytes are/were uploaded.
created_at / updated_atstringISO timestamps.
uploaded_atstring | nullSet when the upload completes.

content_type is a category (place or post), not the file's MIME type. The MIME type lives in the separate mime_type field. This trips people up — they are two different things.

Playback manifest

{
  "hls": "https://cdn.example.com/vod/abc/playlist.m3u8",
  "dash": "https://cdn.example.com/vod/abc/manifest.mpd",
  "embed": "https://media.example.com/api/v1/playback/{assetId}/embed",
  "thumbnail": "https://media.example.com/api/v1/playback/{assetId}/thumb.jpg"
}

Each field is a URL or null depending on what the provider produced.

Allowed MIME types

mime_type is normalized (lower-cased, parameters like ; charset=… stripped) and must be one of:

GroupTypes
Imageimage/jpeg, image/png, image/webp, image/gif, image/svg+xml
Audioaudio/mpeg, audio/wav, audio/ogg, audio/mp4, audio/aac, audio/webm
Videovideo/mp4, video/webm, video/quicktime, video/x-msvideo, video/x-matroska, video/ogg

A video MIME type creates a stream asset (routed to a provider); anything else creates a file asset (stored in R2). An unsupported type is rejected with 400 BAD_REQUEST.

Upload modes

The upload-url response tells you which mode to use via upload.upload_mode:

ModeForHow you upload
single_presigned_putFiles below the multipart thresholdOne PUT to upload_url.
multipart_presigned_putLarge files (R2)PUT each part URL, then complete with the ETags.
tusBunny videoResumable TUS upload to the provider endpoint.

The service switches to multipart automatically once file_size_bytes exceeds R2_MULTIPART_THRESHOLD (default 100 MB), with a part size of R2_MULTIPART_PART_SIZE (default 10 MB). You can force a mode with the upload_strategy field (auto | single | multipart). See Uploads for the full flows.

Streaming providers

Video (stream) assets are delivered by one of three providers, selected via stream_provider (falling back to the service's DEFAULT_STREAM_PROVIDER):

ProviderUploadTranscode & delivery
bunnyResumable TUS to BunnyBunny Stream CDN (signed HLS)
cloudflarePresigned R2 PUT → Cloudflare StreamCloudflare Stream (signed HLS/DASH)
onpremPresigned R2 PUTYour own encoder → HLS/DASH served back through this service

Stream lifecycle

A video's stream_status advances as the provider works, driven by webhooks:

pending_upload  →  processing  →  ready
                                └→ failed
  • pending_upload — asset created, waiting for the bytes.
  • processing — provider received the video and is transcoding.
  • ready — playback URLs are available (playback is populated).
  • failed — transcoding failed; inspect provider logs.

Only when stream_status is ready will playback and batch endpoints return usable URLs.

On this page