Skip to content

Job Lifecycle

Every transcoding job moves through a series of states from creation to completion (or failure). Understanding these states helps you build robust integrations.

When you create a job, it begins as queued and progresses through these states:

queued → probing → planning → transcoding → merging → completed
↘ (optional segmenting on legacy paths)
↘ failed
↘ rejected
↘ cancelled
StateDescription
queuedJob accepted, waiting to be picked up
probingAnalyzing the input file (codec, duration, resolution, byte-range support)
planningDetermining the processing strategy
segmentingLegacy state — not used on current engine paths (inputs are range-read or staged once)
transcodingActively processing media (longest phase)
mergingAssembling chunk outputs into the final file (parallel jobs only)
completedDone — output is ready for download
failedSomething went wrong — check error for details
rejectedInput was rejected before processing (e.g., unsupported format or unsafe command)
cancelledYou cancelled the job before it finished

A job is terminal when it reaches one of: completed, failed, rejected, or cancelled. Terminal jobs cannot transition to another state (except via retry).

The progress field is a number from 0 to 100 that updates as the job runs:

{
"jobId": "01JXYZ...",
"state": "transcoding",
"progress": 65
}
  • 0 — job is queued or just started
  • 1–99 — actively processing
  • 100 — completed

The simplest way to track a job is to poll GET /api/v1/jobs/:id until the state is terminal:

Terminal window
# Poll every 2 seconds
while true; do
RESPONSE=$(curl -s https://api.xora.sh/v1/jobs/JOB_ID \
-H "Authorization: Bearer YOUR_API_KEY")
STATE=$(echo "$RESPONSE" | jq -r '.state')
PROGRESS=$(echo "$RESPONSE" | jq -r '.progress')
echo "State: $STATE, Progress: $PROGRESS%"
if [ "$STATE" = "completed" ] || [ "$STATE" = "failed" ] || \
[ "$STATE" = "rejected" ] || [ "$STATE" = "cancelled" ]; then
echo "$RESPONSE" | jq .
break
fi
sleep 2
done

Instead of polling, pass a webhookUrl when creating a job. Xora will POST the result to your URL when the job reaches a terminal state:

Terminal window
curl -X POST https://api.xora.sh/v1/jobs \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "recipe",
"input": { "url": "https://example.com/video.mp4" },
"output": { "format": "mp4" },
"recipe": { "name": "compress", "crf": 28 },
"webhookUrl": "https://your-server.com/xora-webhook"
}'

See the Webhooks guide for payload format and configuration.

Stop a running job before it completes:

Terminal window
curl -X POST https://api.xora.sh/v1/jobs/JOB_ID/cancel \
-H "Authorization: Bearer YOUR_API_KEY"
{
"cancelled": true
}

Retry a job that ended in failed, rejected, or cancelled state. The retry uses the same input parameters:

Terminal window
curl -X POST https://api.xora.sh/v1/jobs/JOB_ID/retry \
-H "Authorization: Bearer YOUR_API_KEY"
{
"retried": true,
"jobId": "01JXYZ1234ABCDEF56789000"
}

Remove the hosted output files from storage. This is permanent:

Terminal window
curl -X DELETE https://api.xora.sh/v1/jobs/JOB_ID/output \
-H "Authorization: Bearer YOUR_API_KEY"
{
"jobId": "01JXYZ1234ABCDEF56789000",
"deleted": true
}

Fetch a paginated list of your jobs, optionally filtered by state:

Terminal window
# List recent jobs
curl "https://api.xora.sh/v1/jobs?limit=20&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
# Filter by state
curl "https://api.xora.sh/v1/jobs?state=completed&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"jobs": [
{
"id": "01JXYZ...",
"mode": "recipe",
"recipe_name": "compress",
"state": "completed",
"created_at": "2026-06-04T10:00:00.000Z",
"completed_at": "2026-06-04T10:00:45.000Z",
"duration_seconds": 120.5
}
],
"limit": 20,
"offset": 0
}
ParameterTypeDefaultDescription
limitnumber20Results per page (max 100)
offsetnumber0Skip this many results
statestringFilter by state: queued, probing, planning, segmenting, transcoding, merging, completed, failed, rejected, cancelled