Migrate a real-time avatar prototype to Sume async jobs: what changes
Moving from a live avatar session to Sume means replacing a stream with submit, poll and fetch. The code changes, the UX changes, and a Node example that runs.
Moving a real-time avatar prototype to Sume means replacing a continuous session with three calls: submit a job, poll its status, and fetch the result. Sume avatars are async jobs: you submit a request, get a job id back, and read the finished video later. There is no live video session. The rest of your app changes less than you expect, but the experience for the viewer changes in one big way: they get a finished clip, not a conversation.
This post lists what you remove, what you add, and gives a Node program that runs the whole loop.
What you remove and what you add
| In a live prototype | With Sume Avatar 1.0 |
|---|---|
| Open a session or socket | POST /v1/avatar-1.0/talking-video returns 202 with a job id |
| Stream audio in, video out | Send a script or video_inputs; Sume makes the speech and the video |
| React to partial events | Poll /v1/jobs/:id/status until terminal is true, or use a webhook |
| Show the stream | Read /v1/jobs/:id/result and play the media.sume.com URL |
| Reconnect on failure | Retry a submit with the same Idempotency-Key; never resubmit a paid job |
| Per-minute session cost | Per-second job cost, reserved at submit and refunded on failure |
The loop in Node
The program below submits a talking video in async mode, honors next_poll_after_seconds, stops when terminal is true, and reads the result only when the job's sume_status is completed. It needs Node 18 or later for fetch, and your key in SUME_API_KEY. The Idempotency-Key means a retry after a network failure returns the original job.
const API = "https://api.sume.com";
const auth = { Authorization: `Bearer ${process.env.SUME_API_KEY}` };
const sleep = (s) => new Promise((r) => setTimeout(r, s * 1000));
async function main() {
const body = {
avatar_handle: "product_host",
script: "Your order shipped today. Here is how to set it up in two minutes.",
quality: "standard",
mode: "async",
};
const res = await fetch(`${API}/v1/avatar-1.0/talking-video`, {
method: "POST",
headers: { ...auth, "Content-Type": "application/json", "Idempotency-Key": "ship-note-001" },
body: JSON.stringify(body),
});
const job = await res.json();
let s;
do {
await sleep(job.next_poll_after_seconds ?? 5);
s = await (await fetch(`${API}/v1/jobs/${job.request_id}/status`, { headers: auth })).json();
} while (!s.terminal);
if (s.sume_status !== "completed") throw new Error(`job ended ${s.sume_status}`);
const out = await fetch(`${API}/v1/jobs/${job.request_id}/result`, { headers: auth });
console.log(JSON.stringify(await out.json(), null, 2));
}
main();What changes in the product
Your loading state becomes a waiting state with real statuses. There is no way to speak to the avatar, so interaction moves to a form, a chat, or a human. The script becomes the review surface: because the video is built from text, you can read, approve or edit every word before you pay for the render, and you can first approve the stills with an Avatar video preview.
Cost moves from time spent in a session to seconds of finished video. A 30-second clip at plus is $7.35 no matter how many people watch it later.
What to keep from the prototype
Keep the script writing, the persona, the disclosure text and the analytics. Replace the transport. If your prototype included turn-taking, move that logic into the script, with one job per prepared reply. If the prototype's value was the conversation itself, async clips will not replace it, and you should use Sume for the prepared parts only.
Errors and retries
A network error on submit is the first thing to handle. Send an Idempotency-Key on every submit and reuse the same key and the same payload when you retry, and the retry returns the original job instead of billing a second one. A client-side timeout while polling is different: it does not cancel the job, which keeps running and billing, so your fix is to poll again with the stored job id.
A request can also be refused before a job exists. 402 insufficient_credits means Sume cannot reserve the estimated cost, so there is nothing to poll. A terminal failed job carries a public error, and the reservation is released or refunded where applicable, so read the error, fix the input, and submit a new request with a new key.
A migration order that works
- Create the avatar once and store its handle.
- Render one clip with the loop above and play the result in your own player.
- Add the waiting state with real statuses.
- Switch to webhook mode and keep polling as a backup.
- Add captions and a synthetic-media label.
- Only then retire the live prototype's transport code.
Sources
Related posts
More in Developers
- MiniMax H3 Max API in Python: reference-to-video with three images
Use MiniMax H3 Max on Sume from Python: send three product photos in input_references, ask for 768p, poll and save. Reference limits and 10-second price.
- MiniMax H3 text-to-video rejects adaptive: send an explicit ratio
On Sume the H3 text-to-video builder accepts 21:9, 16:9, 4:3, 1:1, 3:4 or 9:16 and rejects adaptive. Only reference-to-video allows it. Preflight in Python.
- Model an AI generation job as a state machine in your database
A schema and update rule for tracking Sume jobs: five statuses, sticky terminal states, a separate webhook delivery column, and the idempotency key on the row.
- Move a Seedance 2.5 call from video-router/generate to /v1/videos
Same jobs, same ids: map reference_image_urls to input_references and image_url to frame_images when you move a seedance-2.5 call to POST /v1/videos.
Written by Sume