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
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/assets | Create an asset shell |
| GET | /v1/assets | List, cursor paginated |
| GET | /v1/assets/{id} | Fetch one, includes state |
| PUT | /v1/assets/{id}/source | Upload the source bytes |
| POST | /v1/assets/{id}/retranscode | Re-run with another profile |
| DELETE | /v1/assets/{id} | Remove asset and renditions |
States
draft → uploaded → probing → transcoding →
ready, 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
| Event | When |
|---|---|
| asset.ready | All renditions written and playlist published |
| asset.failed | Probe or transcode gave up |
| storage.pressure | Free 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
| Code | Meaning |
|---|---|
| 400 invalid_request | Body failed validation, see field |
| 401 unauthenticated | Missing or unknown token |
| 409 wrong_state | Operation not legal in the asset's current state |
| 413 source_too_large | Above limits.max_source_bytes |
| 422 undecodable | ffprobe found no usable video stream |
| 429 slow_down | Per-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.