Genace

Video generation API job states: map remote snapshots to product behavior

Video generation API job states are the contract between a background render and your product. Genace currently uses queued, running, succeeded, failed, and canceled. Keeping these values intact in your application makes incident diagnosis easier than compressing everything into a single loading boolean.

The HTTP layer and the job layer describe different things. A job GET returning HTTP 200 means that the snapshot was retrieved successfully. Its state can still be failed. Conversely, an HTTP 503 while reading a running job does not prove that the job changed state at all.

Build an explicit state table

Remote stateProduct behaviorCollection action
queuedShow accepted or waitingContinue collecting the same job
runningShow renderingContinue collecting; progress may be absent
succeededShow preparing download until storage finishesValidate and copy returned outputs
failedShow a render failureSave the structured error and reconcile credits
canceledShow cancellationStop collection and reconcile the saved result

The first two states are active. The final three are terminal. Do not assume the visible sequence always moves neatly from queued to running: an adapter may report an upstream queue snapshot after the submission service initially marked the local row running. Terminal-state handling should be stable even if active snapshots change order.

Separate rendering from delivery

A succeeded job is not necessarily a file safely stored in your system. Maintain an additional application delivery status such as not_started, copying, available, or copy_failed. If the download fails, retry the download rather than generating another video.

function classify(job) {
  switch (job.state) {
    case 'queued':
    case 'running':
      return { action: 'wait', id: job.id };
    case 'succeeded':
      return { action: 'copy', outputs: job.outputs ?? [] };
    case 'failed':
      return { action: 'report', error: job.error ?? null };
    case 'canceled':
      return { action: 'stop' };
    default:
      return { action: 'investigate', state: job.state };
  }
}

The default branch is intentional defensive behavior. An unexpected future state should generate a diagnostic and preserve the snapshot, rather than being treated as success or silently discarded. The example is application logic, not an official SDK.

Keep accounting fields attached

The serializer exposes credits_held and nullable credits_actual. The submission service deducts credits before it submits upstream. Its terminal update path records the held amount as actual consumption for success and zero for failure or cancellation after the refund path runs. Retain these returned values for reconciliation instead of inferring a balance from a UI label.

Common mistakes include treating null outputs as an empty successful result, using a timer to change running into failed, and retrying canceled jobs automatically. Those shortcuts confuse remote outcomes with local preferences and can spend additional credits without a fresh decision.

Verification checklist

Run the classifier over one fixture per state, an unknown state, and a succeeded fixture with null outputs. Verify that no active state produces a download and no failed state becomes a generic network exception. Confirm that file-copy retries remain attached to the original job.

Continue with failed job responses, output storage, and cost budgeting.

Continue with production operations

Where these facts come from

  • codebase: src/ai/jobs/service.ts
  • codebase: src/ai/jobs/http.ts
  • Editorial keyword hypothesis; search volume not measured