result_ready vs terminal vs completed: gate the Sume result fetch

Poll a Sume job until terminal, fetch the result only when result_ready is true. Failed and canceled jobs answer 409 job_not_completed on /result.

4 min readSume
All posts

Stop polling on terminal, and fetch the result only when result_ready is true. A failed or canceled Sume job is terminal, but GET /v1/jobs/{id}/result returns 409 job_not_completed for it, so a loop that fetches the result on every terminal state will treat a failure as an error in the wrong place. Read status, result and error from the job record instead.

The three flags

A Sume job moves through queued, processing, then completed, failed or canceled. The envelope adds terminal and result_ready, and a queue-shaped status (IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELED) that mirrors sume_status.

The OpenRouter video guide, read today, has four statuses for its job: pending, in_progress, completed, failed. A client written for it that waits for only completed or failed should also handle canceled on Sume.

Status vocabularies, read 2026-10-08
Sume statusTerminalResult fetchOpenRouter equivalent
queuedNo409pending
processingNo409in_progress
completedYes200completed
failedYes409 job_not_completedfailed
canceledYes409 job_not_completedNot listed on this page

A loop that branches once

The helper waitForJob resolves with the job record from /v1/jobs/:id, not with /result, for this reason. It throws SumeJobTimeoutError after 20 minutes by default; the timeout does not cancel the job.

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

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

export async function finish(jobId: string) {
  const job = await waitForJob(jobId, { client });
  if (job.status === "completed") return job.result;
  throw new Error(`job ${jobId} ended ${job.status}: ${JSON.stringify(job.error)}`);
}

Where the media lives

A completed job returns artifacts on media.sume.com. Those URLs are durable, so you can store the URL and not copy the file in a hurry.

  • Do not poll faster than next_poll_after_seconds.
  • A job is readable only by the member whose key created it.
  • Cancel works only before generation starts; otherwise 409 job_generation_already_started.

Webhook consumers get the same split

The webhook event names the outcome already: job.completed, job.failed or job.canceled. Only the first has artifacts in its payload. The second and third carry an error object and status: "ERROR". So a receiver does not need to call /result at all for a completed job; the artifact list is in the event.

For polling code the same rule is status === "completed". Use result_ready when you want one flag that is true exactly when the result can be read, and use terminal only to stop the loop.

Edge cases

Cancel is possible only before generation starts. A cancel call after that returns 409 job_generation_already_started, and the job continues to its own end, so do not mark it canceled in your database until the record says so. A job is readable only by the member whose key created it, so read it with that key.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume