Face swap job completed but no video_url: resource_status

For Avatar Face Swap, poll job_status but read the video only when resource_status is ready. How to poll /v1/jobs and fetch the result without an empty URL.

4 min readSume
All posts

Sume's face swap docs tell you to prefer resource_status for readiness and job_status for polling. Completed resources expose a public-safe video_url and artifacts under media.sume.com when ready, so read the video only when resource_status says it is ready, not the moment the job looks done. Polling the job and reading the resource are two different checks.

The behavior is described on the Face swap (Beta) page and the general rules on Jobs and results.

What is the difference between job_status and resource_status?

The job is the unit of work: its statuses are queued, processing, completed, failed, and canceled. The resource is the face-swap object the job produces. The docs say completed resources carry the video when ready, so the resource can be the thing you wait for. This post does not claim the two ever disagree for long; it only follows the docs' advice to check the right field for the right question.

Which field answers which question for Avatar Face Swap (read 2026-10-02)
QuestionFieldWhere
Is the work still going?job_statusGET /v1/jobs/{id}/status (sume_status)
Is the video ready to download?resource_statusThe face-swap resource
Where is the video?video_url under media.sume.comGET /v1/jobs/{id}/result
Did it end badly?failed or canceledJob status and public error

How should I poll?

Poll GET /v1/jobs/{id}/status with exponential backoff and stop on completed, failed, or canceled (the status response also has terminal and result_ready booleans, and sume_status carries the same values as the table above). Then fetch GET /v1/jobs/{id}/result. Results exist only after completion; before that the API returns a 409 job_not_completed conflict, not an empty body. Do not resubmit the original paid request because your own process timed out, since that creates a second job.

JOB=job_123
while true; do
  S=$(curl -s https://api.sume.com/v1/jobs/$JOB/status \
    -H "Authorization: Bearer $SUME_API_KEY" | jq -r .sume_status)
  case "$S" in completed|failed|canceled) break ;; esac
  sleep 10
done
echo "job status: $S"
curl -s https://api.sume.com/v1/jobs/$JOB/result \
  -H "Authorization: Bearer $SUME_API_KEY" | jq .

What if I use a webhook or sync?

Face swap accepts the same communication modes as other generation submits: async (the default), sync or subscribe with wait_timeout_seconds, and webhook with a public HTTPS webhook_url. A webhook removes the loop, and the webhooks page covers delivery. In every mode, fetch the result by job id and use the Sume media.sume.com URL, not a raw provider URL.

Sync and subscribe wait at most 30 seconds on the submit call, and the Jobs page says face-swap jobs routinely outlast that, so plan for the async or webhook path and keep polling status_url as a backup.

What should I store?

Store the job id as soon as submit returns, and the Sume video_url once the result is ready. Sume mirrors generated outputs into its own media URLs before exposing them, and the Media inputs page tells integrations to store the Sume URL, not provider URLs. If the result call returns a conflict, the job is not complete yet: wait, do not retry the create.

What about the avatar handle?

One more reason a submit does nothing useful: the avatar_handle has to be a ready Avatar 1.0 identity. The face swap page lists that as a precondition, and it recommends the separate Avatar video endpoint if you want a script-driven talking video instead. If your handle is not ready, fix that first; polling a job will not help.

The result's video_url should be a media.sume.com URL. If you see anything else, do not use it, and report it with the job id.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume