> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bitstudio.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Track jobs and image versions

> Know which ID to poll and when an asynchronous image or video operation is actually complete.

Use [`POST /v1/jobs`](/api-reference/jobs/create) for new integrations. Every supported operation returns `202` with the same `jobs` array. Each job ID identifies the new work, even when an edit or upscale also creates a version of an existing image.

## Submit and poll

```json theme={null}
{
  "operation": "edit",
  "image_id": "11111111-1111-4111-8111-111111111111",
  "input": {
    "prompt": "Change the background to a warm terracotta studio wall.",
    "model": "nano-banana-2",
    "resolution": "standard"
  }
}
```

Save every ID in `jobs` and call [`GET /v1/jobs/{id}`](/api-reference/jobs/get). HTTP `202` means accepted; it does not mean the media is ready.

| Status                    | What your application should do                         |
| ------------------------- | ------------------------------------------------------- |
| `pending` or `generating` | Keep the ID and continue polling.                       |
| `completed`               | Read `result.url`, `result.width`, and `result.height`. |
| `failed`                  | Stop polling and record `error.code`.                   |
| `policy_blocked`          | Stop polling and ask for revised inputs.                |

`result` is `null` until the output is complete and has a media URL. `error` is `null` unless the job failed or was blocked. Error codes are safe classifications; support unknown values with a general failure message.

Start with a five-second polling interval and a client deadline. Keep the job IDs after the deadline so polling can resume; video may need longer than a still image. The [quickstart](/api-reference/quickstart) includes a complete polling helper.

## Use a result in the next operation

On completion, `result.image_id` equals the job ID. For an **image result**, pass it as `image_id` to edit, inpaint, upscale, or animate that image. Video results are ready for playback or download; they are not valid sources for these image operations.

Assets and uploaded source images keep their existing APIs. Uploaded images are not processing jobs; inspect them with [`GET /images/{id}`](/api-reference/images/get).

## Existing image endpoints

The existing `/images/*` and `/text-to-video` endpoints remain available with their original response shapes. Current clients can continue using them. When moving to the jobs API, wrap the existing operation body in `input`, set `operation`, and move any source image from the URL to the envelope’s `image_id`.

### Know the existing response shape

| Operation                         | Success response                  | What to poll                    |
| --------------------------------- | --------------------------------- | ------------------------------- |
| Generate                          | `202`, array of images            | Every returned image `id`       |
| Try-on, default                   | `202`, array of images            | Every returned image `id`       |
| Try-on with `create_version=true` | `202`, parent image with versions | New version’s `source_image_id` |
| Edit or inpaint                   | `202`, parent image with versions | New version’s `source_image_id` |
| Upscale                           | `200`, parent image with versions | New version’s `source_image_id` |
| Image-to-video, Kling 3           | `200`, new standalone video image | Returned image `id`             |
| Image-to-video, Kling 2.5         | `200`, parent image with versions | New version’s `source_image_id` |
| Text-to-video                     | `200`, new standalone video image | Returned image `id`             |

For the existing image shape, call [`GET /images/{id}`](/api-reference/images/get). For several jobs, use [`POST /images/batch`](/api-reference/images/batch) with up to 100 IDs. Match batch results by `id`; inaccessible or missing records are not placeholders in the returned array.

### Read existing image states

| Status                              | What your application should do                                      |
| ----------------------------------- | -------------------------------------------------------------------- |
| `pending`                           | Keep the job ID and wait.                                            |
| `generating`                        | Continue polling with a bounded interval.                            |
| `completed`                         | Read `path` and the output dimensions.                               |
| `failed`                            | Stop polling. Record `failure_code` and `fail_reason` when present.  |
| `policy_blocked` or `nsfw_filtered` | Stop polling. Ask for revised inputs; do not automatically resubmit. |

A five-second polling interval is a reasonable starting point for a small integration. Set a client deadline and retain job IDs when it expires so you can resume. Video may need a longer deadline than a still image.

## An edit does not reset the original image

An original image can remain `completed` throughout an edit. The response includes a new version linked to a separate pending image job:

```json theme={null}
{
  "id": "ORIGINAL_IMAGE_ID",
  "status": "completed",
  "versions": [
    {
      "id": "NEW_VERSION_ID",
      "image_id": "ORIGINAL_IMAGE_ID",
      "source_image_id": "EDIT_JOB_ID",
      "version_type": "edited",
      "status": "pending"
    }
  ]
}
```

The identifiers above illustrate their roles; the service returns UUIDs. Poll `EDIT_JOB_ID`, then retrieve the parent again to display its updated versions. A response can include older versions too.

For a single edit, compare version IDs before and after submitting. This excerpt uses the `request` and `waitForJob` helpers from the [quickstart](/api-reference/quickstart):

```javascript theme={null}
const before = await request(`/images/${imageId}`);
const previous = new Set((before.versions || []).map(version => version.id));
const parent = await request(`/images/${imageId}/edit`, {
  method: 'POST',
  body: {
    prompt: 'Change the background to a pale studio wall.',
    model: 'nano-banana-2',
    resolution: 'standard',
    num_images: 1,
  },
});
const added = (parent.versions || []).filter(
  version => !previous.has(version.id) && version.source_image_id,
);
if (added.length !== 1) {
  throw new Error('Could not uniquely identify this edit. Check versions before retrying.');
}
const versionId = added[0].id;
const result = await waitForJob(added[0].source_image_id);
```

Serialize version-creating operations on the same parent while using this comparison. Concurrent edits can make multiple versions appear between the two reads. Store the parent ID, version ID, and job ID together in your application.

To edit a specific previous result, pass its `version_id` with the next request. Omitting it uses the original source image, not automatically the most recent version.

## Recover without creating duplicates

Retrying a read is different from retrying a creation request. If a POST times out, it may already have created a job. Keep any returned IDs and check recent images or parent versions before submitting again. Do not assume an idempotency header is supported.

[Handle errors and account actions →](/api-reference/errors)
