Skip to content

Lifecycle and statuses ​

A conversion is a job with a status. It starts in uploading, passes through analysis and the encode, and ends in ready or error. This page lists every status the API can return, what it means, and what your code should do while a conversion is in it, then covers progress, the liveness signal that tells a working job from a dead one, and how long to wait before you give up.

The path a job takes ​

text
uploading --analyze--> analyzing --> analyzed --start--> waiting --> queued --> running --> ready
    |                      |            |                              |          |
    +----------------------+------------+------------------------------+----------+--> error

waiting is skipped when none of your own jobs is ahead of this one, and a conversion with a preset moves from analyzed to the next status by itself. A job can end in error from any of its working statuses. ready and error are terminal: nothing changes after them except that the output expires (see Retention).

Every status ​

StatusWhat it meansHolds a concurrency slotWhat your client should do
uploadingThe conversion exists and the server is waiting for the master's bytes.YesUpload the master, then call POST /v1/conversions/{id}/analyze. See Uploading large masters.
analyzingThe converter is reading the master to discover its duration, programmes, beds, objects and source kind. No encode has started.YesWait. progress moves from 0 to 100 during this pass.
analyzedThe plan is ready. With a preset on create, the server starts the encode from here without being asked. Without one, it waits for you.YesTwo-step flow: read the conversion, then POST /v1/conversions/{id}/start. One-step flow: wait. If the master declares several programmes and you named none, the conversion stays here until you start it with a programme. See Auto-start and two-step.
waitingA preset is chosen and the job is queued behind your own account's other encodes.NoWait. This is designed behaviour, however long it lasts, and the server does not time it out.
queuedA preset is confirmed and the job is handed to the converter, which has not started it yet.YesWait. This is a capacity wait.
runningThe converter is encoding.YesWait and watch progress.
readyThe IAMF file exists. output, outputProfile, outputLoudnessLufs and expiresAt are filled in.NoDownload it, within 72 hours.
errorThe job failed. errorMessage says why. The minutes for a failed job are not counted against your allowance.NoRead errorMessage, fix the cause, create a new conversion.

GET /v1/usage reports limits.inFlight, the number of conversions holding a slot, next to limits.concurrent. A create is refused with concurrency-limit only when you are at that limit.

GET /v1/conversions?status= filters the list by any one of these values.

What the job reports while it works ​

Four fields describe progress. All of them are on the conversion returned by GET /v1/conversions/{id} and carried in every webhook payload.

FieldMeaning
statusOne of the eight values above. Match on this.
progressA whole number from 0 to 100. It restarts at 0 when the encode begins, because analysis and encoding are separate passes.
stageA finer step inside a status, or null. The converter writes analyzing, analyzed, encoding, uploading (the finished file is being stored; progress is 100) and done. Treat stage as a label for people. Do not branch on it.
updatedAtWhen the row last changed. See below.

errorMessage is null unless the status is error.

updatedAt is the liveness signal ​

Some passes of a long encode, the loudness measurement for example, can run for minutes without changing progress. A job that is not moving is not necessarily dead. While the converter is analysing or encoding it touches the conversion every 60 seconds, which moves updatedAt and nothing else. So:

  • status, progress or updatedAt changing means the job is alive, even if progress has not moved.
  • None of the three changing for a long time, outside waiting and queued, means it is stuck.

This is the rule wait() in the SDK applies. It raises ConversionStalledError when none of the three has changed for stallMs, which defaults to 300 000 ms (five minutes), and it exempts queued and waiting because those are waits by design. Implement the same rule if you poll yourself.

ts
// A hand-written version of the stall rule.
let last = { status: "", progress: -1, updatedAt: "" };
let lastChange = Date.now();

async function poll(id: string): Promise<void> {
  for (;;) {
    const c = await esfera.conversions.get(id);
    if (c.status === "ready") return;
    if (c.status === "error") throw new Error(c.errorMessage ?? "conversion failed");

    const moved =
      c.status !== last.status || c.progress !== last.progress || c.updatedAt !== last.updatedAt;
    if (moved || c.status === "waiting" || c.status === "queued") lastChange = Date.now();
    else if (Date.now() - lastChange > 300_000) throw new Error(`stalled at ${c.status}`);

    last = { status: c.status, progress: c.progress, updatedAt: c.updatedAt };
    await new Promise((r) => setTimeout(r, 3000));
  }
}

Stalls and timeouts ​

The server has its own backstop. A conversion in analyzing, queued or running that has made no progress for 30 minutes is marked failed, and so is one in analyzed that already has a preset and is waiting for the converter to continue it. The conversion ends in error with the message "Timed out — no result from the converter." and its minutes are not counted. A conversion that never receives its master is failed the same way after a period of inactivity. A conversion in analyzed with no preset chosen is failed after 24 hours with "Expired — the conversion was never confirmed. Upload the file again to retry." The conversion.failed webhook for a job failed this way can arrive a little after the status changes.

What this means for a client:

  • Pick a stall threshold well under 30 minutes. Five minutes is the SDK default and is long enough for the 60-second heartbeat to land several times.
  • Set your own overall deadline as well. The API gives a job no maximum run time of its own beyond the 30-minute no-progress rule. wait() takes timeoutMs for this.
  • Do not time out waiting or queued on the stall rule. Time them out on your own deadline if you need one.
  • When a conversion is error, create a new one. A failed conversion cannot be restarted.
  • Poll at three seconds or slower. The limit is 120 requests per minute per key, shared with every other call you make. Webhooks remove the need to poll: see Events and payload.

developers.esfera.audio · the Esfera Converter API, SDK and CLI