Skip to main content
A job is one unit of media work. You describe it, submit it, and it runs on its own while you poll or wait for a webhook. Everything on Rendobar is a job, whether that is an FFmpeg command or animated captions. They all submit the same way and they all return the same shape. Here is the whole round trip:

The shape of a job

Every job is a type plus the inputs and params that type expects:
{ "type": "ffmpeg", "inputs": { "source": "https://..." }, "params": { "command": "ffmpeg -i source out.mp4" } }
type picks the operation, inputs maps names to source URLs, and params carries the options for that type. POST it to /jobs and you get back an id and a waiting status. Each job type documents its own inputs and params.

The lifecycle

A job moves through six statuses. Three are terminal.
StatusWhat it means
waitingSaved and queued. The submit gate passed: auth, plan and rate limits, a balance check, and schema validation.
dispatchedSent to a runner with presigned URLs for download and upload.
runningThe runner is downloading inputs, running the work, and uploading the result.
completeFinished. The output is ready.
failedErrored. Read error.code and error.message.
cancelledStopped before it finished.
On any terminal status the job is billed for the compute it used, and a webhook fires if you configured one. Failures carry an error.code from the error catalogue. A job that runs past its type’s time limit fails with RUNNER_TIMEOUT and is not charged.

The output

A complete job carries one output. The shape is the same for every type, so you never branch on the job type to find the result.
{
  "data": <job-type-specific JSON> | null,
  "file": <File> | null,
  "files": <File>[],
  "expiresAt": <unix ms> | null
}
data
object | null
The computed answer for jobs that return one, such as a probe result or a transcript. null for jobs that only write files.
file
File | null
The headline result you play or download: a single file, or a stream manifest (.m3u8 / .mpd). null for data-only jobs and pure file sets. Always one of files.
files
File[]
Every file the job produced. One entry for a single output, the manifest plus segments for a stream, every member for a set. [] for data-only jobs.
expiresAt
integer | null
Unix ms when the URLs expire. Re-fetch the job with GET /jobs/{id} to mint fresh ones.
Expecting one output? Read output.file.url and you are done. Handling a job that can write many files (frames, HLS segments, a resolution ladder)? Iterate output.files. The two always agree: file, when set, is one of files.

The File type

Each entry in file and files is the same shape:
{
  "url": "https://api.rendobar.com/dl/job_abc123?token=<token>",
  "path": "output.mp4",
  "type": "video",
  "size": 4194304,
  "meta": { "format": "mp4", "width": 1280, "height": 720, "durationMs": 30000 }
}
url is a signed link that expires at output.expiresAt. path is the filename the job wrote. type is an open enum (video, image, audio, captions, playlist, data, other), so tolerate values you do not recognize. size is bytes, and meta carries probed format, width, height, and durationMs when known.

Four patterns, one shape

Which fields are populated depends on what the job produced.
The common case for transform jobs. file is the output, files lists it, data is null.
{
  "data": null,
  "file": { "url": "https://api.rendobar.com/dl/job_abc123?token=<token>", "path": "output.mp4", "type": "video", "size": 4194304 },
  "files": [
    { "url": "https://api.rendobar.com/dl/job_abc123?token=<token>", "path": "output.mp4", "type": "video", "size": 4194304 }
  ],
  "expiresAt": 1735689600000
}

Reading it

The read path is the same every time. No job-type discriminator.
const { data: job } = await res.json();

if (job.output.data) console.log(job.output.data);   // computed answer
if (job.output.file) console.log(job.output.file.url); // the thing to play or download
for (const f of job.output.files) console.log(f.path, f.type, f.size); // the full list
Three invariants hold for every type, so you can code against them: output.file is always one of output.files (or null), expiresAt is set whenever files is non-empty, and a complete job always has output.data or output.files. It never returns nothing.

Polling or webhooks

Poll GET /jobs/{id} every second or two until the status is terminal, or skip polling entirely: register a webhook and Rendobar pushes job.completed to your server the moment the job lands.

See also