Sume TypeScript SDK waitForJob: 20-minute timeout, job keeps billing
How @sume-com/sdk waitForJob polls a generation job, what SumeJobTimeoutError means, and why a client timeout does not cancel or refund the job.

waitForJob in @sume-com/sdk polls /v1/jobs/:id until the job is terminal, with a default timeout of 20 minutes, a 2-second poll floor, and a longer interval whenever next_poll_after_seconds asks for one. If the timeout hits first it throws SumeJobTimeoutError, and the job keeps running and keeps billing, because the helper only stops your wait.
The SDK runs page lists the options; this post shows the shape of a production call and the one decision that matters on timeout.
Submit with fetch, wait with the SDK
The SDK client sends x-api-key only, so do not add an Authorization header to its calls. The raw submit below uses Bearer on its own request, which is fine. A /v1/videos job id is a job at /v1/jobs/{id}, which is what waitForJob reads.
import { createSumeClient, waitForJob, SumeJobTimeoutError } from "@sume-com/sdk";
const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });
const res = await fetch("https://api.sume.com/v1/videos", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.SUME_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": "mug-push-in-001",
},
body: JSON.stringify({ model: "wan-3.0", prompt: "Push-in on a ceramic mug", duration: 5 }),
});
const { id } = await res.json();
try {
const job = await waitForJob(id, {
client,
timeout: 25 * 60_000,
onStatus: (status) => console.log(status),
});
console.log(job.status, job.result?.artifacts);
} catch (e) {
if (e instanceof SumeJobTimeoutError) console.log("still running:", e.jobId);
else throw e;
}Behaviors worth knowing
- Generated operations resolve with
{ data, error, response }and do not throw on an API error; the wait helpers do throw. - It resolves with the job record from
/v1/jobs/:id, not/result, so a failed or canceled job gives youstatusanderrordirectly. - The client retries 408, 429 and 5xx twice by default and retries a POST only when it carries an
Idempotency-Key. - Pass an
AbortSignalto stop waiting early; it ends the wait, not the job.
What to do on timeout
Store e.jobId. Read it again later with getApiJob, or cancel it with cancelApiJob if it has not started. Resubmitting the original request is the one wrong answer, since that pays for a second job.
Choosing between helpers
Use waitForJob for generation jobs, which live at /v1/jobs/:id. Use waitForRun for Action, Agent Completion and Format runs, and subscribeFormatRun when you want to create a Format run and wait for it in one call. A job id is not a run id, and the two cannot be swapped. When you can skip waiting entirely, a webhook with verifyWebhook is the better fit.
Settings that matter
timeoutdefaults to 20 minutes for jobs.pollIntervaldefaults to 2 seconds and acts as a floor; a longernext_poll_after_secondswins.signalaborts the wait and the in-flight request.- The client adds jitter to polls so several waiters started together do not stay in phase.
Sources
Related posts
More in Developers
- Webhook endpoint down: redeliver a Sume video job after the retries
Sume retries a job webhook 10 times, 30 seconds apart. If your receiver was down longer, POST /v1/jobs/{id}/webhook/redeliver re-sends the terminal payload.
- waitForJob times out at 20 minutes: why it does not fit a Vercel route
Sume SDK waitForJob waits 20 minutes by default and the job keeps billing if it throws. Vercel functions default to 300 s, so wait in a worker.
- Sume wave_size_hint and a Worker subrequest limit: submit in waves
A Worker fan-out of Sume jobs hits 50 subrequests on Free. Size each wave from generation_limits, not from the hint alone, and stop at queue_capacity_remaining.
- Sume webhook retries: 10 attempts, 30 s apart, 10 s timeout each
The delivery schedule for Sume job webhooks: 10 attempts, fixed 30 s spacing, 10 s timeout, about 4.5 minutes of retries, then redeliver and the status poll.
Written by Sume