API reference

Base URL https://chek.skunkeu.win/v1. JSON in, JSON out. Bearer token in Authorization. Everything is idempotent on Idempotency-Key except the raw source upload.

Assets

MethodPathPurpose
POST/v1/assetsCreate an asset shell
GET/v1/assetsList, cursor paginated
GET/v1/assets/{id}Fetch one, includes state
PUT/v1/assets/{id}/sourceUpload the source bytes
POST/v1/assets/{id}/retranscodeRe-run with another profile
DELETE/v1/assets/{id}Remove asset and renditions

States

draftuploadedprobingtranscodingready, or failed with a reason string. There is no intermediate percentage; ask for ?expand=jobs if you want per-rendition progress.

Example

GET /v1/assets/as_81a4370d?expand=jobs

{
  "id": "as_81a4370d",
  "state": "ready",
  "duration_ms": 333332,
  "profile": "web-1080p",
  "playback": "/p/as_81a4370d/master.m3u8",
  "renditions": [
    {"name":"1080p","bitrate_kbps":4500,"codec":"h264","ready":true},
    {"name":"720p", "bitrate_kbps":2400,"codec":"h264","ready":true},
    {"name":"480p", "bitrate_kbps":1100,"codec":"h264","ready":true}
  ]
}

Pull from a URL instead of uploading

POST /v1/assets
{"profile":"web-1080p","source_url":"https://example.org/raw/reel.mov"}

# -> {"id":"as_0e320470","state":"probing"}

The fetcher follows at most three redirects and refuses anything that is not a direct media response.

Profiles

A profile is a named ladder in config.toml. Changing a profile does not touch assets already transcoded with it — call retranscode for that.

[profile.web-1080p]
container = "hls"
segment_seconds = 4
ladder = [
  { height = 1080, bitrate_kbps = 4500 },
  { height =  720, bitrate_kbps = 2400 },
  { height =  480, bitrate_kbps = 1100 },
]
audio = { codec = "aac", bitrate_kbps = 128 }

Playback

Playback paths live outside /v1 and are served as static files: /p/{asset}/master.m3u8 plus per-rendition playlists and .m4s segments. Segments are immutable and safe to cache for a year; the master playlist is not.

Webhooks

EventWhen
asset.readyAll renditions written and playlist published
asset.failedProbe or transcode gave up
storage.pressureFree space under the configured floor

Delivery is at-least-once with exponential backoff for six hours. Signature is X-Signature: sha256=<hex> over the raw body.

Errors

CodeMeaning
400 invalid_requestBody failed validation, see field
401 unauthenticatedMissing or unknown token
409 wrong_stateOperation not legal in the asset's current state
413 source_too_largeAbove limits.max_source_bytes
422 undecodableffprobe found no usable video stream
429 slow_downPer-token bucket empty, honour Retry-After

Limits

Defaults, all configurable: 32 GB per source, 8 concurrent transcodes, 600 API requests per minute per token. Uploads are not counted against the API bucket.