Avatar submit response: what next_action tells your client to do
next_action has three values on avatar submits: poll_status while queued or processing, fetch_result once completed, inspect_events for failed or canceled jobs.
On an avatar submit or status response, next_action has exactly three values: poll_status while the job is queued or processing, fetch_result once it has completed, and inspect_events when it failed or was canceled. Drive your client from that field and from terminal and result_ready, and you do not need your own table of job states.
The fields to read
The submit response for POST /v1/avatar-1.0/talking-video carries avatar_video_id, status_url, result_url, events_url, cancel_url, cancelable, next_action, next_poll_after_seconds, recommended_poll_interval_seconds, result_ready, terminal, and idempotency_hit. Use the URLs as given and do not rebuild them from ids.
| Job status | next_action | terminal | Client action |
|---|---|---|---|
| queued | poll_status | false | Wait next_poll_after_seconds, GET status_url |
| processing | poll_status | false | Keep polling |
| completed | fetch_result | true | GET result_url when result_ready is true |
| failed | inspect_events | true | GET events_url, fix the input, submit once |
| canceled | inspect_events | true | Stop, or submit a new request deliberately |
A minimal loop
Obey next_poll_after_seconds when it is present and use a backoff when it is not. Cap the loop with a deadline in your own client; a client timeout does not cancel the job, so a deadline only stops the waiting.
import json, os, time, urllib.request
def get(url):
req = urllib.request.Request(url, headers={
"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"User-Agent": "loop/1.0"})
with urllib.request.urlopen(req, timeout=30) as r:
return json.load(r)["data"]
def follow(status_url, deadline=1200):
end, wait = time.time() + deadline, 5
while time.time() < end:
d = get(status_url)
if d["next_action"] == "fetch_result":
return get(d["result_url"])
if d["next_action"] == "inspect_events":
raise RuntimeError("see " + d["events_url"])
wait = d.get("next_poll_after_seconds") or min(wait * 2, 30)
time.sleep(wait)
raise TimeoutError("still running; job continues")Why a field and not a status table
Job statuses (queued, processing, completed, failed, canceled) are useful for display, but they are a poor thing to branch on, since a new state would break a client that did not know it. next_action is the contract: the OpenAPI states it is poll_status for queued and processing jobs, fetch_result once completed, and inspect_events for failed or canceled terminal jobs. A client that handles those three values keeps working if the status list grows.
Keep terminal as the loop exit and result_ready as the gate for reading output. A job can be terminal without a result, for example when it failed, and that is exactly when inspect_events applies. For the resource itself, GET /v1/avatar-videos/{id} returns data.avatar_video and data.job together, which is handy when you want the video fields and the job state in one read.
Common mistakes
- Treating
result_ready: falseas a failure. The job is simply not done. - Resubmitting after a client timeout. Re-read the job from
status_urlor retry the submit with the sameIdempotency-Key. - Reading the video URL before the job completes. Use
fetch_resultas the gate. - Ignoring
inspect_eventson a failed job and sending the same input again. Read the reason first.
Limits
This describes the avatar-video job envelope. Format and agent runs use a different action vocabulary, with none for terminal runs, so do not share one handler across both without branching. The sketch above omits error handling for HTTP errors on purpose; add retries for 5xx in production, and use a webhook if you prefer to be told.
Sources
Related posts
More in Developers
- Avatar sync wait: timed_out vs capacity_exhausted, and what to do
A sync avatar submit can return 2xx with sync.timed_out or sync.capacity_exhausted true. Both mean keep the job: poll status_url, never submit a new paid job.
- Avatar script too long? The 4 to 60 second window and how to split it
Sume accepts an avatar talking video only when the script estimates 4 to 60 seconds. Split longer scripts into scenes or jobs; costs from $0.74 to $33.
- Avatar video 400: script must include at least one word
A Sume talking-video request with an empty or whitespace-only script returns 400 invalid_request, with no job or ledger entry. Guard blank template fields.
- Avatar video 404 "Avatar was not found": handle from another workspace
A talking-video request with an unknown handle, or one from another workspace, returns 404 not_found and no job. Check the handle and the key's workspace.
Written by Sume