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
uploading --analyze--> analyzing --> analyzed --start--> waiting --> queued --> running --> ready
| | | | |
+----------------------+------------+------------------------------+----------+--> errorwaiting 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
| Status | What it means | Holds a concurrency slot | What your client should do |
|---|---|---|---|
uploading | The conversion exists and the server is waiting for the master's bytes. | Yes | Upload the master, then call POST /v1/conversions/{id}/analyze. See Uploading large masters. |
analyzing | The converter is reading the master to discover its duration, programmes, beds, objects and source kind. No encode has started. | Yes | Wait. progress moves from 0 to 100 during this pass. |
analyzed | The plan is ready. With a preset on create, the server starts the encode from here without being asked. Without one, it waits for you. | Yes | Two-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. |
waiting | A preset is chosen and the job is queued behind your own account's other encodes. | No | Wait. This is designed behaviour, however long it lasts, and the server does not time it out. |
queued | A preset is confirmed and the job is handed to the converter, which has not started it yet. | Yes | Wait. This is a capacity wait. |
running | The converter is encoding. | Yes | Wait and watch progress. |
ready | The IAMF file exists. output, outputProfile, outputLoudnessLufs and expiresAt are filled in. | No | Download it, within 72 hours. |
error | The job failed. errorMessage says why. The minutes for a failed job are not counted against your allowance. | No | Read 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.
| Field | Meaning |
|---|---|
status | One of the eight values above. Match on this. |
progress | A whole number from 0 to 100. It restarts at 0 when the encode begins, because analysis and encoding are separate passes. |
stage | A 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. |
updatedAt | When 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,progressorupdatedAtchanging means the job is alive, even ifprogresshas not moved.- None of the three changing for a long time, outside
waitingandqueued, 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.
// 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()takestimeoutMsfor this. - Do not time out
waitingorqueuedon 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.
Related
- Retention: what happens 72 hours after a job reaches a terminal status.
GET /v1/conversions/{id}and the Conversion object.- Errors for
already-started,concurrency-limitand the others a lifecycle call can return.