Multipart form upload. Field name is file. The response tells you where the asset will live.
curl -X POST https://cdn.example.com/upload \
-H "x-api-key: <UPLOAD_KEY>" \
-F "file=@/path/to/clip.mp4"
# image response (ready immediately):
{"url":"https://cdn.example.com/i/<guid>.jpg","status":"ready","kind":"image"}
# video response (HLS transcode is running in the background):
{"url":"https://cdn.example.com/<guid>/index.m3u8","status":"processing","kind":"video"}
Every GET requires x-api-key from CDN_API_KEYS. Use it as an Authorization-style header; image/video tags need a fetch-then-blob step or an HLS player with xhrSetup.
curl -H "x-api-key: <CDN_KEY>" https://cdn.example.com/i/<guid>.jpg -o out.jpg
?status)Poll the same URL you'll eventually play, just append ?status (or ?done). Returns JSON; status is one of processing, ready, failed, unknown.
curl -H "x-api-key: <CDN_KEY>" "https://cdn.example.com/<guid>/index.m3u8?status"
# in flight:
{"guid":"…","status":"processing","percent":42.1,"elapsed_seconds":8.4,
"duration_seconds":20.0,"out_seconds":8.42,"speed":1.15,"eta_seconds":10.06}
# done:
{"guid":"…","status":"ready","percent":100}
# failed (stays for 60s after failure, then 404):
{"guid":"…","status":"failed","error":"ffmpeg: exit status 1\n…"}
async function upload(file, uploadKey, cdnKey) {
const fd = new FormData();
fd.append('file', file);
const r = await fetch('/upload', {
method: 'POST',
headers: { 'x-api-key': uploadKey },
body: fd,
});
if (!r.ok) throw new Error('upload ' + r.status);
const { url, kind, status } = await r.json();
if (status === 'ready') return { url, kind };
while (true) {
await new Promise(r => setTimeout(r, 1500));
const s = await (await fetch(url + '?status', {
headers: { 'x-api-key': cdnKey },
})).json();
if (s.status === 'ready') return { url, kind };
if (s.status === 'failed') throw new Error(s.error);
// optionally surface s.percent / s.eta_seconds in your UI here
}
}
Since <video> can't set custom headers, use hls.js with xhrSetup:
const hls = new Hls({
xhrSetup: xhr => xhr.setRequestHeader('x-api-key', CDN_KEY),
});
hls.loadSource(url);
hls.attachMedia(document.querySelector('video'));