waitForRun or waitForJob? Pick by which Sume endpoint made the id

A Sume run id cannot be read as a job id. Which create endpoint made your id decides waitForRun or waitForJob, with a TypeScript example of each.

5 min readSume
All posts

Use waitForJob when the id came from a generation endpoint such as POST /v1/image-1.0/generate or POST /v1/video-1.0/generate. Use waitForRun when the id came from a Format, Action, or Agent run. Sume says you cannot use a job id as a run id, or a run id as a job id, so a wrong pick shows up as a failed read, not as a slow one.

Both helpers ship in @sume-com/sdk 0.2.0. They look alike, but they poll different URLs and they take different options.

Decide by the create call

The table is from the Waiting for runs and jobs page, read 2026-10-09.

Which wait helper fits which id, as of 2026-10-09.
Id came fromSurfacePoll pathHelperDefault timeout
POST /v1/formats/.../runsFormat run/v1/format-runs/{id}waitForRun with family: "format"10 minutes
Action runAction run/v1/action-runs/{id}waitForRun with family: "action"10 minutes
Agent runAgent run/v1/agent-runs/{id}waitForRun with family: "agent"10 minutes
POST /v1/image-1.0/generate, /v1/video-1.0/generate, Avatar routesGeneration job/v1/jobs/{id}waitForJob20 minutes
subscribeFormatRunFormat run (create and wait)/v1/format-runs/{id}The helper itself20 minutes

What differs in the code

family is required on waitForRun and the helper cannot infer it, because a run id does not show its surface. waitForJob has no such option. It also treats pollInterval as a floor: when next_poll_after_seconds in the status payload asks for longer, that wins. Pass client to both, since the module default has no base URL or key.

import { createSumeClient, generateVideoV1, waitForJob, waitForRun } from "@sume-com/sdk";

const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });

// Generation job: the id is request_id from the submit response.
const { data, error } = await generateVideoV1({
  client,
  headers: { "idempotency-key": crypto.randomUUID() },
  body: { prompt: "Slow push-in on a ceramic mug", mode: "async" },
});
if (error) throw new Error(JSON.stringify(error));
const job = await waitForJob(data!.data.request_id, { client });
console.log(job.status, job.result.artifacts);

// Format run: you must say which family the id belongs to.
declare const runId: string;
const run = await waitForRun(runId, { client, family: "format" });
console.log(run.status, run.primary_output_url);

What each one returns on failure

Neither helper treats a terminal failure as an exception. waitForJob resolves with the record from /v1/jobs/{id}, not from /result, because for failed and canceled jobs /result answers 409 job_not_completed and there would be nothing to hand back. Read status, result, and error from the record.

The helpers do throw on timeouts and request errors: SumeRunTimeoutError and SumeRunRequestError for runs, SumeJobTimeoutError and SumeJobRequestError for jobs. A timeout does not cancel the work. The job keeps running and keeps billing, so store the id, read it again later, or cancel it.

A quick check helps when you inherit code with a bare id. Look at where it was stored: an id saved from a /v1/jobs response is a job id, and an id saved from a *-runs response is a run id. If you cannot tell, read the id from each surface once, and expect a not-found from the wrong one. Do not guess a family in production code; keep the family next to the id in your own table.

If you can skip the wait, do. Both the runs page and the jobs page recommend webhooks for production, and the SDK has no event stream today: subscribe there means create, then poll.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume